| File: | src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp |
| Warning: | line 75, column 31 Forming reference to null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | //===-- MinidumpParser.cpp ------------------------------------------------===// | |||
| 2 | // | |||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
| 4 | // See https://llvm.org/LICENSE.txt for license information. | |||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
| 6 | // | |||
| 7 | //===----------------------------------------------------------------------===// | |||
| 8 | ||||
| 9 | #include "MinidumpParser.h" | |||
| 10 | #include "NtStructures.h" | |||
| 11 | #include "RegisterContextMinidump_x86_32.h" | |||
| 12 | ||||
| 13 | #include "Plugins/Process/Utility/LinuxProcMaps.h" | |||
| 14 | #include "lldb/Utility/LLDBAssert.h" | |||
| 15 | #include "lldb/Utility/Log.h" | |||
| 16 | ||||
| 17 | // C includes | |||
| 18 | // C++ includes | |||
| 19 | #include <algorithm> | |||
| 20 | #include <map> | |||
| 21 | #include <vector> | |||
| 22 | #include <utility> | |||
| 23 | ||||
| 24 | using namespace lldb_private; | |||
| 25 | using namespace minidump; | |||
| 26 | ||||
| 27 | llvm::Expected<MinidumpParser> | |||
| 28 | MinidumpParser::Create(const lldb::DataBufferSP &data_sp) { | |||
| 29 | auto ExpectedFile = llvm::object::MinidumpFile::create( | |||
| 30 | llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump")); | |||
| 31 | if (!ExpectedFile) | |||
| 32 | return ExpectedFile.takeError(); | |||
| 33 | ||||
| 34 | return MinidumpParser(data_sp, std::move(*ExpectedFile)); | |||
| 35 | } | |||
| 36 | ||||
| 37 | MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp, | |||
| 38 | std::unique_ptr<llvm::object::MinidumpFile> file) | |||
| 39 | : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {} | |||
| 40 | ||||
| 41 | llvm::ArrayRef<uint8_t> MinidumpParser::GetData() { | |||
| 42 | return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(), | |||
| 43 | m_data_sp->GetByteSize()); | |||
| 44 | } | |||
| 45 | ||||
| 46 | llvm::ArrayRef<uint8_t> MinidumpParser::GetStream(StreamType stream_type) { | |||
| 47 | return m_file->getRawStream(stream_type) | |||
| 48 | .getValueOr(llvm::ArrayRef<uint8_t>()); | |||
| 49 | } | |||
| 50 | ||||
| 51 | UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) { | |||
| 52 | auto cv_record = | |||
| 53 | GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize); | |||
| 54 | ||||
| 55 | // Read the CV record signature | |||
| 56 | const llvm::support::ulittle32_t *signature = nullptr; | |||
| 57 | Status error = consumeObject(cv_record, signature); | |||
| 58 | if (error.Fail()) | |||
| ||||
| 59 | return UUID(); | |||
| 60 | ||||
| 61 | const CvSignature cv_signature = | |||
| 62 | static_cast<CvSignature>(static_cast<uint32_t>(*signature)); | |||
| 63 | ||||
| 64 | if (cv_signature == CvSignature::Pdb70) { | |||
| 65 | const UUID::CvRecordPdb70 *pdb70_uuid = nullptr; | |||
| 66 | Status error = consumeObject(cv_record, pdb70_uuid); | |||
| 67 | if (error.Fail()) | |||
| 68 | return UUID(); | |||
| 69 | if (GetArchitecture().GetTriple().isOSBinFormatELF()) { | |||
| 70 | if (pdb70_uuid->Age != 0) | |||
| 71 | return UUID::fromOptionalData(pdb70_uuid, sizeof(*pdb70_uuid)); | |||
| 72 | return UUID::fromOptionalData(&pdb70_uuid->Uuid, | |||
| 73 | sizeof(pdb70_uuid->Uuid)); | |||
| 74 | } | |||
| 75 | return UUID::fromCvRecord(*pdb70_uuid); | |||
| ||||
| 76 | } else if (cv_signature == CvSignature::ElfBuildId) | |||
| 77 | return UUID::fromOptionalData(cv_record); | |||
| 78 | ||||
| 79 | return UUID(); | |||
| 80 | } | |||
| 81 | ||||
| 82 | llvm::ArrayRef<minidump::Thread> MinidumpParser::GetThreads() { | |||
| 83 | auto ExpectedThreads = GetMinidumpFile().getThreadList(); | |||
| 84 | if (ExpectedThreads) | |||
| 85 | return *ExpectedThreads; | |||
| 86 | ||||
| 87 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 2))); ::llvm::Error error_private = (ExpectedThreads .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read thread list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 88 | ExpectedThreads.takeError(),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 2))); ::llvm::Error error_private = (ExpectedThreads .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read thread list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 89 | "Failed to read thread list: {0}")do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 2))); ::llvm::Error error_private = (ExpectedThreads .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read thread list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 90 | return {}; | |||
| 91 | } | |||
| 92 | ||||
| 93 | llvm::ArrayRef<uint8_t> | |||
| 94 | MinidumpParser::GetThreadContext(const LocationDescriptor &location) { | |||
| 95 | if (location.RVA + location.DataSize > GetData().size()) | |||
| 96 | return {}; | |||
| 97 | return GetData().slice(location.RVA, location.DataSize); | |||
| 98 | } | |||
| 99 | ||||
| 100 | llvm::ArrayRef<uint8_t> | |||
| 101 | MinidumpParser::GetThreadContext(const minidump::Thread &td) { | |||
| 102 | return GetThreadContext(td.Context); | |||
| 103 | } | |||
| 104 | ||||
| 105 | llvm::ArrayRef<uint8_t> | |||
| 106 | MinidumpParser::GetThreadContextWow64(const minidump::Thread &td) { | |||
| 107 | // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If | |||
| 108 | // the minidump was captured with a 64-bit debugger, then the CONTEXT we just | |||
| 109 | // grabbed from the mini_dump_thread is the one for the 64-bit "native" | |||
| 110 | // process rather than the 32-bit "guest" process we care about. In this | |||
| 111 | // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment | |||
| 112 | // Block) of the 64-bit process. | |||
| 113 | auto teb_mem = GetMemory(td.EnvironmentBlock, sizeof(TEB64)); | |||
| 114 | if (teb_mem.empty()) | |||
| 115 | return {}; | |||
| 116 | ||||
| 117 | const TEB64 *wow64teb; | |||
| 118 | Status error = consumeObject(teb_mem, wow64teb); | |||
| 119 | if (error.Fail()) | |||
| 120 | return {}; | |||
| 121 | ||||
| 122 | // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure | |||
| 123 | // that includes the 32-bit CONTEXT (after a ULONG). See: | |||
| 124 | // https://msdn.microsoft.com/en-us/library/ms681670.aspx | |||
| 125 | auto context = | |||
| 126 | GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32)); | |||
| 127 | if (context.size() < sizeof(MinidumpContext_x86_32)) | |||
| 128 | return {}; | |||
| 129 | ||||
| 130 | return context; | |||
| 131 | // NOTE: We don't currently use the TEB for anything else. If we | |||
| 132 | // need it in the future, the 32-bit TEB is located according to the address | |||
| 133 | // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]). | |||
| 134 | } | |||
| 135 | ||||
| 136 | ArchSpec MinidumpParser::GetArchitecture() { | |||
| 137 | if (m_arch.IsValid()) | |||
| 138 | return m_arch; | |||
| 139 | ||||
| 140 | // Set the architecture in m_arch | |||
| 141 | llvm::Expected<const SystemInfo &> system_info = m_file->getSystemInfo(); | |||
| 142 | ||||
| 143 | if (!system_info) { | |||
| 144 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (system_info .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read SystemInfo stream: {0}"); } else :: llvm::consumeError(::std::move(error_private)); } while (0) | |||
| 145 | system_info.takeError(),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (system_info .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read SystemInfo stream: {0}"); } else :: llvm::consumeError(::std::move(error_private)); } while (0) | |||
| 146 | "Failed to read SystemInfo stream: {0}")do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (system_info .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read SystemInfo stream: {0}"); } else :: llvm::consumeError(::std::move(error_private)); } while (0); | |||
| 147 | return m_arch; | |||
| 148 | } | |||
| 149 | ||||
| 150 | // TODO what to do about big endiand flavors of arm ? | |||
| 151 | // TODO set the arm subarch stuff if the minidump has info about it | |||
| 152 | ||||
| 153 | llvm::Triple triple; | |||
| 154 | triple.setVendor(llvm::Triple::VendorType::UnknownVendor); | |||
| 155 | ||||
| 156 | switch (system_info->ProcessorArch) { | |||
| 157 | case ProcessorArchitecture::X86: | |||
| 158 | triple.setArch(llvm::Triple::ArchType::x86); | |||
| 159 | break; | |||
| 160 | case ProcessorArchitecture::AMD64: | |||
| 161 | triple.setArch(llvm::Triple::ArchType::x86_64); | |||
| 162 | break; | |||
| 163 | case ProcessorArchitecture::ARM: | |||
| 164 | triple.setArch(llvm::Triple::ArchType::arm); | |||
| 165 | break; | |||
| 166 | case ProcessorArchitecture::ARM64: | |||
| 167 | case ProcessorArchitecture::BP_ARM64: | |||
| 168 | triple.setArch(llvm::Triple::ArchType::aarch64); | |||
| 169 | break; | |||
| 170 | default: | |||
| 171 | triple.setArch(llvm::Triple::ArchType::UnknownArch); | |||
| 172 | break; | |||
| 173 | } | |||
| 174 | ||||
| 175 | // TODO add all of the OSes that Minidump/breakpad distinguishes? | |||
| 176 | switch (system_info->PlatformId) { | |||
| 177 | case OSPlatform::Win32S: | |||
| 178 | case OSPlatform::Win32Windows: | |||
| 179 | case OSPlatform::Win32NT: | |||
| 180 | case OSPlatform::Win32CE: | |||
| 181 | triple.setOS(llvm::Triple::OSType::Win32); | |||
| 182 | triple.setVendor(llvm::Triple::VendorType::PC); | |||
| 183 | break; | |||
| 184 | case OSPlatform::Linux: | |||
| 185 | triple.setOS(llvm::Triple::OSType::Linux); | |||
| 186 | break; | |||
| 187 | case OSPlatform::MacOSX: | |||
| 188 | triple.setOS(llvm::Triple::OSType::MacOSX); | |||
| 189 | triple.setVendor(llvm::Triple::Apple); | |||
| 190 | break; | |||
| 191 | case OSPlatform::IOS: | |||
| 192 | triple.setOS(llvm::Triple::OSType::IOS); | |||
| 193 | triple.setVendor(llvm::Triple::Apple); | |||
| 194 | break; | |||
| 195 | case OSPlatform::Android: | |||
| 196 | triple.setOS(llvm::Triple::OSType::Linux); | |||
| 197 | triple.setEnvironment(llvm::Triple::EnvironmentType::Android); | |||
| 198 | break; | |||
| 199 | default: { | |||
| 200 | triple.setOS(llvm::Triple::OSType::UnknownOS); | |||
| 201 | auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA); | |||
| 202 | if (!ExpectedCSD) { | |||
| 203 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (ExpectedCSD .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to CSD Version string: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 204 | ExpectedCSD.takeError(),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (ExpectedCSD .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to CSD Version string: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 205 | "Failed to CSD Version string: {0}")do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (ExpectedCSD .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to CSD Version string: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 206 | } else { | |||
| 207 | if (ExpectedCSD->find("Linux") != std::string::npos) | |||
| 208 | triple.setOS(llvm::Triple::OSType::Linux); | |||
| 209 | } | |||
| 210 | break; | |||
| 211 | } | |||
| 212 | } | |||
| 213 | m_arch.SetTriple(triple); | |||
| 214 | return m_arch; | |||
| 215 | } | |||
| 216 | ||||
| 217 | const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() { | |||
| 218 | llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo); | |||
| 219 | ||||
| 220 | if (data.size() == 0) | |||
| 221 | return nullptr; | |||
| 222 | ||||
| 223 | return MinidumpMiscInfo::Parse(data); | |||
| 224 | } | |||
| 225 | ||||
| 226 | llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() { | |||
| 227 | llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus); | |||
| 228 | ||||
| 229 | if (data.size() == 0) | |||
| 230 | return llvm::None; | |||
| 231 | ||||
| 232 | return LinuxProcStatus::Parse(data); | |||
| 233 | } | |||
| 234 | ||||
| 235 | llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() { | |||
| 236 | const MinidumpMiscInfo *misc_info = GetMiscInfo(); | |||
| 237 | if (misc_info != nullptr) { | |||
| 238 | return misc_info->GetPid(); | |||
| 239 | } | |||
| 240 | ||||
| 241 | llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus(); | |||
| 242 | if (proc_status.hasValue()) { | |||
| 243 | return proc_status->GetPid(); | |||
| 244 | } | |||
| 245 | ||||
| 246 | return llvm::None; | |||
| 247 | } | |||
| 248 | ||||
| 249 | llvm::ArrayRef<minidump::Module> MinidumpParser::GetModuleList() { | |||
| 250 | auto ExpectedModules = GetMinidumpFile().getModuleList(); | |||
| 251 | if (ExpectedModules) | |||
| 252 | return *ExpectedModules; | |||
| 253 | ||||
| 254 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 21))); ::llvm::Error error_private = (ExpectedModules .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read module list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 255 | ExpectedModules.takeError(),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 21))); ::llvm::Error error_private = (ExpectedModules .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read module list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 256 | "Failed to read module list: {0}")do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 21))); ::llvm::Error error_private = (ExpectedModules .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read module list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 257 | return {}; | |||
| 258 | } | |||
| 259 | ||||
| 260 | static bool | |||
| 261 | CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser, | |||
| 262 | std::vector<MemoryRegionInfo> ®ions) { | |||
| 263 | auto data = parser.GetStream(StreamType::LinuxMaps); | |||
| 264 | if (data.empty()) | |||
| 265 | return false; | |||
| 266 | ||||
| 267 | Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS(1u << 8)); | |||
| 268 | ParseLinuxMapRegions( | |||
| 269 | llvm::toStringRef(data), | |||
| 270 | [®ions, &log](llvm::Expected<MemoryRegionInfo> region) -> bool { | |||
| 271 | if (region) | |||
| 272 | regions.push_back(*region); | |||
| 273 | else | |||
| 274 | LLDB_LOG_ERROR(log, region.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (region.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Reading memory region from minidump failed: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
| 275 | "Reading memory region from minidump failed: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (region.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Reading memory region from minidump failed: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0); | |||
| 276 | return true; | |||
| 277 | }); | |||
| 278 | return !regions.empty(); | |||
| 279 | } | |||
| 280 | ||||
| 281 | /// Check for the memory regions starting at \a load_addr for a contiguous | |||
| 282 | /// section that has execute permissions that matches the module path. | |||
| 283 | /// | |||
| 284 | /// When we load a breakpad generated minidump file, we might have the | |||
| 285 | /// /proc/<pid>/maps text for a process that details the memory map of the | |||
| 286 | /// process that the minidump is describing. This checks the sorted memory | |||
| 287 | /// regions for a section that has execute permissions. A sample maps files | |||
| 288 | /// might look like: | |||
| 289 | /// | |||
| 290 | /// 00400000-00401000 r--p 00000000 fd:01 2838574 /tmp/a.out | |||
| 291 | /// 00401000-00402000 r-xp 00001000 fd:01 2838574 /tmp/a.out | |||
| 292 | /// 00402000-00403000 r--p 00002000 fd:01 2838574 /tmp/a.out | |||
| 293 | /// 00403000-00404000 r--p 00002000 fd:01 2838574 /tmp/a.out | |||
| 294 | /// 00404000-00405000 rw-p 00003000 fd:01 2838574 /tmp/a.out | |||
| 295 | /// ... | |||
| 296 | /// | |||
| 297 | /// This function should return true when given 0x00400000 and "/tmp/a.out" | |||
| 298 | /// is passed in as the path since it has a consecutive memory region for | |||
| 299 | /// "/tmp/a.out" that has execute permissions at 0x00401000. This will help us | |||
| 300 | /// differentiate if a file has been memory mapped into a process for reading | |||
| 301 | /// and breakpad ends up saving a minidump file that has two module entries for | |||
| 302 | /// a given file: one that is read only for the entire file, and then one that | |||
| 303 | /// is the real executable that is loaded into memory for execution. For memory | |||
| 304 | /// mapped files they will typically show up and r--p permissions and a range | |||
| 305 | /// matcning the entire range of the file on disk: | |||
| 306 | /// | |||
| 307 | /// 00800000-00805000 r--p 00000000 fd:01 2838574 /tmp/a.out | |||
| 308 | /// 00805000-00806000 r-xp 00001000 fd:01 1234567 /usr/lib/libc.so | |||
| 309 | /// | |||
| 310 | /// This function should return false when asked about 0x00800000 with | |||
| 311 | /// "/tmp/a.out" as the path. | |||
| 312 | /// | |||
| 313 | /// \param[in] path | |||
| 314 | /// The path to the module to check for in the memory regions. Only sequential | |||
| 315 | /// memory regions whose paths match this path will be considered when looking | |||
| 316 | /// for execute permissions. | |||
| 317 | /// | |||
| 318 | /// \param[in] regions | |||
| 319 | /// A sorted list of memory regions obtained from a call to | |||
| 320 | /// CreateRegionsCacheFromLinuxMaps. | |||
| 321 | /// | |||
| 322 | /// \param[in] base_of_image | |||
| 323 | /// The load address of this module from BaseOfImage in the modules list. | |||
| 324 | /// | |||
| 325 | /// \return | |||
| 326 | /// True if a contiguous region of memory belonging to the module with a | |||
| 327 | /// matching path exists that has executable permissions. Returns false if | |||
| 328 | /// \a regions is empty or if there are no regions with execute permissions | |||
| 329 | /// that match \a path. | |||
| 330 | ||||
| 331 | static bool CheckForLinuxExecutable(ConstString path, | |||
| 332 | const MemoryRegionInfos ®ions, | |||
| 333 | lldb::addr_t base_of_image) { | |||
| 334 | if (regions.empty()) | |||
| 335 | return false; | |||
| 336 | lldb::addr_t addr = base_of_image; | |||
| 337 | MemoryRegionInfo region = MinidumpParser::GetMemoryRegionInfo(regions, addr); | |||
| 338 | while (region.GetName() == path) { | |||
| 339 | if (region.GetExecutable() == MemoryRegionInfo::eYes) | |||
| 340 | return true; | |||
| 341 | addr += region.GetRange().GetByteSize(); | |||
| 342 | region = MinidumpParser::GetMemoryRegionInfo(regions, addr); | |||
| 343 | } | |||
| 344 | return false; | |||
| 345 | } | |||
| 346 | ||||
| 347 | std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() { | |||
| 348 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES(1u << 21)); | |||
| 349 | auto ExpectedModules = GetMinidumpFile().getModuleList(); | |||
| 350 | if (!ExpectedModules) { | |||
| 351 | LLDB_LOG_ERROR(log, ExpectedModules.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedModules.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read module list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 352 | "Failed to read module list: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedModules.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read module list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 353 | return {}; | |||
| 354 | } | |||
| 355 | ||||
| 356 | // Create memory regions from the linux maps only. We do this to avoid issues | |||
| 357 | // with breakpad generated minidumps where if someone has mmap'ed a shared | |||
| 358 | // library into memory to accesss its data in the object file, we can get a | |||
| 359 | // minidump with two mappings for a binary: one whose base image points to a | |||
| 360 | // memory region that is read + execute and one that is read only. | |||
| 361 | MemoryRegionInfos linux_regions; | |||
| 362 | if (CreateRegionsCacheFromLinuxMaps(*this, linux_regions)) | |||
| 363 | llvm::sort(linux_regions); | |||
| 364 | ||||
| 365 | // map module_name -> filtered_modules index | |||
| 366 | typedef llvm::StringMap<size_t> MapType; | |||
| 367 | MapType module_name_to_filtered_index; | |||
| 368 | ||||
| 369 | std::vector<const minidump::Module *> filtered_modules; | |||
| 370 | ||||
| 371 | for (const auto &module : *ExpectedModules) { | |||
| 372 | auto ExpectedName = m_file->getString(module.ModuleNameRVA); | |||
| 373 | if (!ExpectedName) { | |||
| 374 | LLDB_LOG_ERROR(log, ExpectedName.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedName.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to get module name: {0}"); } else ::llvm:: consumeError(::std::move(error_private)); } while (0) | |||
| 375 | "Failed to get module name: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedName.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to get module name: {0}"); } else ::llvm:: consumeError(::std::move(error_private)); } while (0); | |||
| 376 | continue; | |||
| 377 | } | |||
| 378 | ||||
| 379 | MapType::iterator iter; | |||
| 380 | bool inserted; | |||
| 381 | // See if we have inserted this module aready into filtered_modules. If we | |||
| 382 | // haven't insert an entry into module_name_to_filtered_index with the | |||
| 383 | // index where we will insert it if it isn't in the vector already. | |||
| 384 | std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace( | |||
| 385 | *ExpectedName, filtered_modules.size()); | |||
| 386 | ||||
| 387 | if (inserted) { | |||
| 388 | // This module has not been seen yet, insert it into filtered_modules at | |||
| 389 | // the index that was inserted into module_name_to_filtered_index using | |||
| 390 | // "filtered_modules.size()" above. | |||
| 391 | filtered_modules.push_back(&module); | |||
| 392 | } else { | |||
| 393 | // We have a duplicate module entry. Check the linux regions to see if | |||
| 394 | // either module is not really a mapped executable. If one but not the | |||
| 395 | // other is a real mapped executable, prefer the executable one. This | |||
| 396 | // can happen when a process mmap's in the file for an executable in | |||
| 397 | // order to read bytes from the executable file. A memory region mapping | |||
| 398 | // will exist for the mmap'ed version and for the loaded executable, but | |||
| 399 | // only one will have a consecutive region that is executable in the | |||
| 400 | // memory regions. | |||
| 401 | auto dup_module = filtered_modules[iter->second]; | |||
| 402 | ConstString name(*ExpectedName); | |||
| 403 | bool is_executable = | |||
| 404 | CheckForLinuxExecutable(name, linux_regions, module.BaseOfImage); | |||
| 405 | bool dup_is_executable = | |||
| 406 | CheckForLinuxExecutable(name, linux_regions, dup_module->BaseOfImage); | |||
| 407 | ||||
| 408 | if (is_executable != dup_is_executable) { | |||
| 409 | if (is_executable) | |||
| 410 | filtered_modules[iter->second] = &module; | |||
| 411 | continue; | |||
| 412 | } | |||
| 413 | // This module has been seen. Modules are sometimes mentioned multiple | |||
| 414 | // times when they are mapped discontiguously, so find the module with | |||
| 415 | // the lowest "base_of_image" and use that as the filtered module. | |||
| 416 | if (module.BaseOfImage < dup_module->BaseOfImage) | |||
| 417 | filtered_modules[iter->second] = &module; | |||
| 418 | } | |||
| 419 | } | |||
| 420 | return filtered_modules; | |||
| 421 | } | |||
| 422 | ||||
| 423 | const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() { | |||
| 424 | auto ExpectedStream = GetMinidumpFile().getExceptionStream(); | |||
| 425 | if (ExpectedStream) | |||
| 426 | return &*ExpectedStream; | |||
| 427 | ||||
| 428 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (ExpectedStream .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read minidump exception stream: {0}"); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
| 429 | ExpectedStream.takeError(),do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (ExpectedStream .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read minidump exception stream: {0}"); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
| 430 | "Failed to read minidump exception stream: {0}")do { ::lldb_private::Log *log_private = (GetLogIfAnyCategoriesSet ((1u << 1))); ::llvm::Error error_private = (ExpectedStream .takeError()); if (log_private && error_private) { log_private ->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read minidump exception stream: {0}"); } else ::llvm::consumeError(::std::move(error_private)); } while (0); | |||
| 431 | return nullptr; | |||
| 432 | } | |||
| 433 | ||||
| 434 | llvm::Optional<minidump::Range> | |||
| 435 | MinidumpParser::FindMemoryRange(lldb::addr_t addr) { | |||
| 436 | llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List); | |||
| 437 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES(1u << 21)); | |||
| 438 | ||||
| 439 | auto ExpectedMemory = GetMinidumpFile().getMemoryList(); | |||
| 440 | if (!ExpectedMemory) { | |||
| 441 | LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedMemory.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read memory list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 442 | "Failed to read memory list: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedMemory.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read memory list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 443 | } else { | |||
| 444 | for (const auto &memory_desc : *ExpectedMemory) { | |||
| 445 | const LocationDescriptor &loc_desc = memory_desc.Memory; | |||
| 446 | const lldb::addr_t range_start = memory_desc.StartOfMemoryRange; | |||
| 447 | const size_t range_size = loc_desc.DataSize; | |||
| 448 | ||||
| 449 | if (loc_desc.RVA + loc_desc.DataSize > GetData().size()) | |||
| 450 | return llvm::None; | |||
| 451 | ||||
| 452 | if (range_start <= addr && addr < range_start + range_size) { | |||
| 453 | auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc); | |||
| 454 | if (!ExpectedSlice) { | |||
| 455 | LLDB_LOG_ERROR(log, ExpectedSlice.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedSlice.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to get memory slice: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 456 | "Failed to get memory slice: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedSlice.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to get memory slice: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 457 | return llvm::None; | |||
| 458 | } | |||
| 459 | return minidump::Range(range_start, *ExpectedSlice); | |||
| 460 | } | |||
| 461 | } | |||
| 462 | } | |||
| 463 | ||||
| 464 | // Some Minidumps have a Memory64ListStream that captures all the heap memory | |||
| 465 | // (full-memory Minidumps). We can't exactly use the same loop as above, | |||
| 466 | // because the Minidump uses slightly different data structures to describe | |||
| 467 | // those | |||
| 468 | ||||
| 469 | if (!data64.empty()) { | |||
| 470 | llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; | |||
| 471 | uint64_t base_rva; | |||
| 472 | std::tie(memory64_list, base_rva) = | |||
| 473 | MinidumpMemoryDescriptor64::ParseMemory64List(data64); | |||
| 474 | ||||
| 475 | if (memory64_list.empty()) | |||
| 476 | return llvm::None; | |||
| 477 | ||||
| 478 | for (const auto &memory_desc64 : memory64_list) { | |||
| 479 | const lldb::addr_t range_start = memory_desc64.start_of_memory_range; | |||
| 480 | const size_t range_size = memory_desc64.data_size; | |||
| 481 | ||||
| 482 | if (base_rva + range_size > GetData().size()) | |||
| 483 | return llvm::None; | |||
| 484 | ||||
| 485 | if (range_start <= addr && addr < range_start + range_size) { | |||
| 486 | return minidump::Range(range_start, | |||
| 487 | GetData().slice(base_rva, range_size)); | |||
| 488 | } | |||
| 489 | base_rva += range_size; | |||
| 490 | } | |||
| 491 | } | |||
| 492 | ||||
| 493 | return llvm::None; | |||
| 494 | } | |||
| 495 | ||||
| 496 | llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr, | |||
| 497 | size_t size) { | |||
| 498 | // I don't have a sense of how frequently this is called or how many memory | |||
| 499 | // ranges a Minidump typically has, so I'm not sure if searching for the | |||
| 500 | // appropriate range linearly each time is stupid. Perhaps we should build | |||
| 501 | // an index for faster lookups. | |||
| 502 | llvm::Optional<minidump::Range> range = FindMemoryRange(addr); | |||
| 503 | if (!range) | |||
| 504 | return {}; | |||
| 505 | ||||
| 506 | // There's at least some overlap between the beginning of the desired range | |||
| 507 | // (addr) and the current range. Figure out where the overlap begins and how | |||
| 508 | // much overlap there is. | |||
| 509 | ||||
| 510 | const size_t offset = addr - range->start; | |||
| 511 | ||||
| 512 | if (addr < range->start || offset >= range->range_ref.size()) | |||
| 513 | return {}; | |||
| 514 | ||||
| 515 | const size_t overlap = std::min(size, range->range_ref.size() - offset); | |||
| 516 | return range->range_ref.slice(offset, overlap); | |||
| 517 | } | |||
| 518 | ||||
| 519 | static bool | |||
| 520 | CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser, | |||
| 521 | std::vector<MemoryRegionInfo> ®ions) { | |||
| 522 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES(1u << 21)); | |||
| 523 | auto ExpectedInfo = parser.GetMinidumpFile().getMemoryInfoList(); | |||
| 524 | if (!ExpectedInfo) { | |||
| 525 | LLDB_LOG_ERROR(log, ExpectedInfo.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedInfo.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read memory info list: {0}"); } else :: llvm::consumeError(::std::move(error_private)); } while (0) | |||
| 526 | "Failed to read memory info list: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedInfo.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read memory info list: {0}"); } else :: llvm::consumeError(::std::move(error_private)); } while (0); | |||
| 527 | return false; | |||
| 528 | } | |||
| 529 | constexpr auto yes = MemoryRegionInfo::eYes; | |||
| 530 | constexpr auto no = MemoryRegionInfo::eNo; | |||
| 531 | for (const MemoryInfo &entry : *ExpectedInfo) { | |||
| 532 | MemoryRegionInfo region; | |||
| 533 | region.GetRange().SetRangeBase(entry.BaseAddress); | |||
| 534 | region.GetRange().SetByteSize(entry.RegionSize); | |||
| 535 | ||||
| 536 | MemoryProtection prot = entry.Protect; | |||
| 537 | region.SetReadable(bool(prot & MemoryProtection::NoAccess) ? no : yes); | |||
| 538 | region.SetWritable( | |||
| 539 | bool(prot & (MemoryProtection::ReadWrite | MemoryProtection::WriteCopy | | |||
| 540 | MemoryProtection::ExecuteReadWrite | | |||
| 541 | MemoryProtection::ExeciteWriteCopy)) | |||
| 542 | ? yes | |||
| 543 | : no); | |||
| 544 | region.SetExecutable( | |||
| 545 | bool(prot & (MemoryProtection::Execute | MemoryProtection::ExecuteRead | | |||
| 546 | MemoryProtection::ExecuteReadWrite | | |||
| 547 | MemoryProtection::ExeciteWriteCopy)) | |||
| 548 | ? yes | |||
| 549 | : no); | |||
| 550 | region.SetMapped(entry.State != MemoryState::Free ? yes : no); | |||
| 551 | regions.push_back(region); | |||
| 552 | } | |||
| 553 | return !regions.empty(); | |||
| 554 | } | |||
| 555 | ||||
| 556 | static bool | |||
| 557 | CreateRegionsCacheFromMemoryList(MinidumpParser &parser, | |||
| 558 | std::vector<MemoryRegionInfo> ®ions) { | |||
| 559 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES(1u << 21)); | |||
| 560 | auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList(); | |||
| 561 | if (!ExpectedMemory) { | |||
| 562 | LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedMemory.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read memory list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0) | |||
| 563 | "Failed to read memory list: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (ExpectedMemory.takeError()); if (log_private && error_private ) { log_private->FormatError(::std::move(error_private), "/usr/src/gnu/usr.bin/clang/liblldbPluginProcess/../../../llvm/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp" , __func__, "Failed to read memory list: {0}"); } else ::llvm ::consumeError(::std::move(error_private)); } while (0); | |||
| 564 | return false; | |||
| 565 | } | |||
| 566 | regions.reserve(ExpectedMemory->size()); | |||
| 567 | for (const MemoryDescriptor &memory_desc : *ExpectedMemory) { | |||
| 568 | if (memory_desc.Memory.DataSize == 0) | |||
| 569 | continue; | |||
| 570 | MemoryRegionInfo region; | |||
| 571 | region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange); | |||
| 572 | region.GetRange().SetByteSize(memory_desc.Memory.DataSize); | |||
| 573 | region.SetReadable(MemoryRegionInfo::eYes); | |||
| 574 | region.SetMapped(MemoryRegionInfo::eYes); | |||
| 575 | regions.push_back(region); | |||
| 576 | } | |||
| 577 | regions.shrink_to_fit(); | |||
| 578 | return !regions.empty(); | |||
| 579 | } | |||
| 580 | ||||
| 581 | static bool | |||
| 582 | CreateRegionsCacheFromMemory64List(MinidumpParser &parser, | |||
| 583 | std::vector<MemoryRegionInfo> ®ions) { | |||
| 584 | llvm::ArrayRef<uint8_t> data = | |||
| 585 | parser.GetStream(StreamType::Memory64List); | |||
| 586 | if (data.empty()) | |||
| 587 | return false; | |||
| 588 | llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; | |||
| 589 | uint64_t base_rva; | |||
| 590 | std::tie(memory64_list, base_rva) = | |||
| 591 | MinidumpMemoryDescriptor64::ParseMemory64List(data); | |||
| 592 | ||||
| 593 | if (memory64_list.empty()) | |||
| 594 | return false; | |||
| 595 | ||||
| 596 | regions.reserve(memory64_list.size()); | |||
| 597 | for (const auto &memory_desc : memory64_list) { | |||
| 598 | if (memory_desc.data_size == 0) | |||
| 599 | continue; | |||
| 600 | MemoryRegionInfo region; | |||
| 601 | region.GetRange().SetRangeBase(memory_desc.start_of_memory_range); | |||
| 602 | region.GetRange().SetByteSize(memory_desc.data_size); | |||
| 603 | region.SetReadable(MemoryRegionInfo::eYes); | |||
| 604 | region.SetMapped(MemoryRegionInfo::eYes); | |||
| 605 | regions.push_back(region); | |||
| 606 | } | |||
| 607 | regions.shrink_to_fit(); | |||
| 608 | return !regions.empty(); | |||
| 609 | } | |||
| 610 | ||||
| 611 | std::pair<MemoryRegionInfos, bool> MinidumpParser::BuildMemoryRegions() { | |||
| 612 | // We create the region cache using the best source. We start with | |||
| 613 | // the linux maps since they are the most complete and have names for the | |||
| 614 | // regions. Next we try the MemoryInfoList since it has | |||
| 615 | // read/write/execute/map data, and then fall back to the MemoryList and | |||
| 616 | // Memory64List to just get a list of the memory that is mapped in this | |||
| 617 | // core file | |||
| 618 | MemoryRegionInfos result; | |||
| 619 | const auto &return_sorted = [&](bool is_complete) { | |||
| 620 | llvm::sort(result); | |||
| 621 | return std::make_pair(std::move(result), is_complete); | |||
| 622 | }; | |||
| 623 | if (CreateRegionsCacheFromLinuxMaps(*this, result)) | |||
| 624 | return return_sorted(true); | |||
| 625 | if (CreateRegionsCacheFromMemoryInfoList(*this, result)) | |||
| 626 | return return_sorted(true); | |||
| 627 | if (CreateRegionsCacheFromMemoryList(*this, result)) | |||
| 628 | return return_sorted(false); | |||
| 629 | CreateRegionsCacheFromMemory64List(*this, result); | |||
| 630 | return return_sorted(false); | |||
| 631 | } | |||
| 632 | ||||
| 633 | #define ENUM_TO_CSTR(ST)case StreamType::ST: return "ST" \ | |||
| 634 | case StreamType::ST: \ | |||
| 635 | return #ST | |||
| 636 | ||||
| 637 | llvm::StringRef | |||
| 638 | MinidumpParser::GetStreamTypeAsString(StreamType stream_type) { | |||
| 639 | switch (stream_type) { | |||
| 640 | ENUM_TO_CSTR(Unused)case StreamType::Unused: return "Unused"; | |||
| 641 | ENUM_TO_CSTR(ThreadList)case StreamType::ThreadList: return "ThreadList"; | |||
| 642 | ENUM_TO_CSTR(ModuleList)case StreamType::ModuleList: return "ModuleList"; | |||
| 643 | ENUM_TO_CSTR(MemoryList)case StreamType::MemoryList: return "MemoryList"; | |||
| 644 | ENUM_TO_CSTR(Exception)case StreamType::Exception: return "Exception"; | |||
| 645 | ENUM_TO_CSTR(SystemInfo)case StreamType::SystemInfo: return "SystemInfo"; | |||
| 646 | ENUM_TO_CSTR(ThreadExList)case StreamType::ThreadExList: return "ThreadExList"; | |||
| 647 | ENUM_TO_CSTR(Memory64List)case StreamType::Memory64List: return "Memory64List"; | |||
| 648 | ENUM_TO_CSTR(CommentA)case StreamType::CommentA: return "CommentA"; | |||
| 649 | ENUM_TO_CSTR(CommentW)case StreamType::CommentW: return "CommentW"; | |||
| 650 | ENUM_TO_CSTR(HandleData)case StreamType::HandleData: return "HandleData"; | |||
| 651 | ENUM_TO_CSTR(FunctionTable)case StreamType::FunctionTable: return "FunctionTable"; | |||
| 652 | ENUM_TO_CSTR(UnloadedModuleList)case StreamType::UnloadedModuleList: return "UnloadedModuleList"; | |||
| 653 | ENUM_TO_CSTR(MiscInfo)case StreamType::MiscInfo: return "MiscInfo"; | |||
| 654 | ENUM_TO_CSTR(MemoryInfoList)case StreamType::MemoryInfoList: return "MemoryInfoList"; | |||
| 655 | ENUM_TO_CSTR(ThreadInfoList)case StreamType::ThreadInfoList: return "ThreadInfoList"; | |||
| 656 | ENUM_TO_CSTR(HandleOperationList)case StreamType::HandleOperationList: return "HandleOperationList"; | |||
| 657 | ENUM_TO_CSTR(Token)case StreamType::Token: return "Token"; | |||
| 658 | ENUM_TO_CSTR(JavascriptData)case StreamType::JavascriptData: return "JavascriptData"; | |||
| 659 | ENUM_TO_CSTR(SystemMemoryInfo)case StreamType::SystemMemoryInfo: return "SystemMemoryInfo"; | |||
| 660 | ENUM_TO_CSTR(ProcessVMCounters)case StreamType::ProcessVMCounters: return "ProcessVMCounters"; | |||
| 661 | ENUM_TO_CSTR(LastReserved)case StreamType::LastReserved: return "LastReserved"; | |||
| 662 | ENUM_TO_CSTR(BreakpadInfo)case StreamType::BreakpadInfo: return "BreakpadInfo"; | |||
| 663 | ENUM_TO_CSTR(AssertionInfo)case StreamType::AssertionInfo: return "AssertionInfo"; | |||
| 664 | ENUM_TO_CSTR(LinuxCPUInfo)case StreamType::LinuxCPUInfo: return "LinuxCPUInfo"; | |||
| 665 | ENUM_TO_CSTR(LinuxProcStatus)case StreamType::LinuxProcStatus: return "LinuxProcStatus"; | |||
| 666 | ENUM_TO_CSTR(LinuxLSBRelease)case StreamType::LinuxLSBRelease: return "LinuxLSBRelease"; | |||
| 667 | ENUM_TO_CSTR(LinuxCMDLine)case StreamType::LinuxCMDLine: return "LinuxCMDLine"; | |||
| 668 | ENUM_TO_CSTR(LinuxEnviron)case StreamType::LinuxEnviron: return "LinuxEnviron"; | |||
| 669 | ENUM_TO_CSTR(LinuxAuxv)case StreamType::LinuxAuxv: return "LinuxAuxv"; | |||
| 670 | ENUM_TO_CSTR(LinuxMaps)case StreamType::LinuxMaps: return "LinuxMaps"; | |||
| 671 | ENUM_TO_CSTR(LinuxDSODebug)case StreamType::LinuxDSODebug: return "LinuxDSODebug"; | |||
| 672 | ENUM_TO_CSTR(LinuxProcStat)case StreamType::LinuxProcStat: return "LinuxProcStat"; | |||
| 673 | ENUM_TO_CSTR(LinuxProcUptime)case StreamType::LinuxProcUptime: return "LinuxProcUptime"; | |||
| 674 | ENUM_TO_CSTR(LinuxProcFD)case StreamType::LinuxProcFD: return "LinuxProcFD"; | |||
| 675 | ENUM_TO_CSTR(FacebookAppCustomData)case StreamType::FacebookAppCustomData: return "FacebookAppCustomData"; | |||
| 676 | ENUM_TO_CSTR(FacebookBuildID)case StreamType::FacebookBuildID: return "FacebookBuildID"; | |||
| 677 | ENUM_TO_CSTR(FacebookAppVersionName)case StreamType::FacebookAppVersionName: return "FacebookAppVersionName"; | |||
| 678 | ENUM_TO_CSTR(FacebookJavaStack)case StreamType::FacebookJavaStack: return "FacebookJavaStack"; | |||
| 679 | ENUM_TO_CSTR(FacebookDalvikInfo)case StreamType::FacebookDalvikInfo: return "FacebookDalvikInfo"; | |||
| 680 | ENUM_TO_CSTR(FacebookUnwindSymbols)case StreamType::FacebookUnwindSymbols: return "FacebookUnwindSymbols"; | |||
| 681 | ENUM_TO_CSTR(FacebookDumpErrorLog)case StreamType::FacebookDumpErrorLog: return "FacebookDumpErrorLog"; | |||
| 682 | ENUM_TO_CSTR(FacebookAppStateLog)case StreamType::FacebookAppStateLog: return "FacebookAppStateLog"; | |||
| 683 | ENUM_TO_CSTR(FacebookAbortReason)case StreamType::FacebookAbortReason: return "FacebookAbortReason"; | |||
| 684 | ENUM_TO_CSTR(FacebookThreadName)case StreamType::FacebookThreadName: return "FacebookThreadName"; | |||
| 685 | ENUM_TO_CSTR(FacebookLogcat)case StreamType::FacebookLogcat: return "FacebookLogcat"; | |||
| 686 | } | |||
| 687 | return "unknown stream type"; | |||
| 688 | } | |||
| 689 | ||||
| 690 | MemoryRegionInfo | |||
| 691 | MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos ®ions, | |||
| 692 | lldb::addr_t load_addr) { | |||
| 693 | MemoryRegionInfo region; | |||
| 694 | auto pos = llvm::upper_bound(regions, load_addr); | |||
| 695 | if (pos != regions.begin() && | |||
| 696 | std::prev(pos)->GetRange().Contains(load_addr)) { | |||
| 697 | return *std::prev(pos); | |||
| 698 | } | |||
| 699 | ||||
| 700 | if (pos == regions.begin()) | |||
| 701 | region.GetRange().SetRangeBase(0); | |||
| 702 | else | |||
| 703 | region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd()); | |||
| 704 | ||||
| 705 | if (pos == regions.end()) | |||
| 706 | region.GetRange().SetRangeEnd(UINT64_MAX0xffffffffffffffffULL); | |||
| 707 | else | |||
| 708 | region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase()); | |||
| 709 | ||||
| 710 | region.SetReadable(MemoryRegionInfo::eNo); | |||
| 711 | region.SetWritable(MemoryRegionInfo::eNo); | |||
| 712 | region.SetExecutable(MemoryRegionInfo::eNo); | |||
| 713 | region.SetMapped(MemoryRegionInfo::eNo); | |||
| 714 | return region; | |||
| 715 | } |
| 1 | //===-- MinidumpTypes.h -----------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H |
| 10 | #define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H |
| 11 | |
| 12 | #include "lldb/Utility/Status.h" |
| 13 | |
| 14 | #include "llvm/ADT/ArrayRef.h" |
| 15 | #include "llvm/ADT/BitmaskEnum.h" |
| 16 | #include "llvm/ADT/Optional.h" |
| 17 | #include "llvm/ADT/SmallVector.h" |
| 18 | #include "llvm/ADT/StringRef.h" |
| 19 | #include "llvm/BinaryFormat/Minidump.h" |
| 20 | #include "llvm/Support/ConvertUTF.h" |
| 21 | #include "llvm/Support/Endian.h" |
| 22 | |
| 23 | // C includes |
| 24 | // C++ includes |
| 25 | |
| 26 | // Reference: |
| 27 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx |
| 28 | // https://chromium.googlesource.com/breakpad/breakpad/ |
| 29 | |
| 30 | namespace lldb_private { |
| 31 | |
| 32 | namespace minidump { |
| 33 | |
| 34 | using namespace llvm::minidump; |
| 35 | |
| 36 | LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()using ::llvm::BitmaskEnumDetail::operator~; using ::llvm::BitmaskEnumDetail ::operator|; using ::llvm::BitmaskEnumDetail::operator&; using ::llvm::BitmaskEnumDetail::operator^; using ::llvm::BitmaskEnumDetail ::operator|=; using ::llvm::BitmaskEnumDetail::operator&= ; using ::llvm::BitmaskEnumDetail::operator^=; |
| 37 | |
| 38 | enum class CvSignature : uint32_t { |
| 39 | Pdb70 = 0x53445352, // RSDS |
| 40 | ElfBuildId = 0x4270454c, // BpEL (Breakpad/Crashpad minidumps) |
| 41 | }; |
| 42 | |
| 43 | enum class MinidumpMiscInfoFlags : uint32_t { |
| 44 | ProcessID = (1 << 0), |
| 45 | ProcessTimes = (1 << 1), |
| 46 | LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes)LLVM_BITMASK_LARGEST_ENUMERATOR = ProcessTimes |
| 47 | }; |
| 48 | |
| 49 | template <typename T> |
| 50 | Status consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) { |
| 51 | Status error; |
| 52 | if (Buffer.size() < sizeof(T)) { |
| 53 | error.SetErrorString("Insufficient buffer!"); |
| 54 | return error; |
| 55 | } |
| 56 | |
| 57 | Object = reinterpret_cast<const T *>(Buffer.data()); |
| 58 | Buffer = Buffer.drop_front(sizeof(T)); |
| 59 | return error; |
| 60 | } |
| 61 | |
| 62 | struct MinidumpMemoryDescriptor64 { |
| 63 | llvm::support::ulittle64_t start_of_memory_range; |
| 64 | llvm::support::ulittle64_t data_size; |
| 65 | |
| 66 | static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> |
| 67 | ParseMemory64List(llvm::ArrayRef<uint8_t> &data); |
| 68 | }; |
| 69 | static_assert(sizeof(MinidumpMemoryDescriptor64) == 16, |
| 70 | "sizeof MinidumpMemoryDescriptor64 is not correct!"); |
| 71 | |
| 72 | // TODO misc2, misc3 ? |
| 73 | // Reference: |
| 74 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx |
| 75 | struct MinidumpMiscInfo { |
| 76 | llvm::support::ulittle32_t size; |
| 77 | // flags1 represents what info in the struct is valid |
| 78 | llvm::support::ulittle32_t flags1; |
| 79 | llvm::support::ulittle32_t process_id; |
| 80 | llvm::support::ulittle32_t process_create_time; |
| 81 | llvm::support::ulittle32_t process_user_time; |
| 82 | llvm::support::ulittle32_t process_kernel_time; |
| 83 | |
| 84 | static const MinidumpMiscInfo *Parse(llvm::ArrayRef<uint8_t> &data); |
| 85 | |
| 86 | llvm::Optional<lldb::pid_t> GetPid() const; |
| 87 | }; |
| 88 | static_assert(sizeof(MinidumpMiscInfo) == 24, |
| 89 | "sizeof MinidumpMiscInfo is not correct!"); |
| 90 | |
| 91 | // The /proc/pid/status is saved as an ascii string in the file |
| 92 | class LinuxProcStatus { |
| 93 | public: |
| 94 | llvm::StringRef proc_status; |
| 95 | lldb::pid_t pid; |
| 96 | |
| 97 | static llvm::Optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data); |
| 98 | |
| 99 | lldb::pid_t GetPid() const; |
| 100 | |
| 101 | private: |
| 102 | LinuxProcStatus() = default; |
| 103 | }; |
| 104 | |
| 105 | } // namespace minidump |
| 106 | } // namespace lldb_private |
| 107 | #endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H |