blob: adaa01c76d36b174fc3d38bfc90133d2de91c452 [file] [log] [blame]
Dimitar Vlahovski36e21a32016-10-05 18:11:45 +00001//===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
Dimitar Vlahovski36e21a32016-10-05 18:11:45 +00008//===----------------------------------------------------------------------===//
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +00009
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000010#include "MinidumpParser.h"
Dimitar Vlahovski7b18dd42016-10-31 15:35:18 +000011#include "NtStructures.h"
12#include "RegisterContextMinidump_x86_32.h"
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000013
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +000014#include "lldb/Target/MemoryRegionInfo.h"
Leonard Mosescu2ae3ec32018-07-12 17:27:18 +000015#include "lldb/Utility/LLDBAssert.h"
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +000016
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000017// C includes
18// C++ includes
Leonard Mosescu2ae3ec32018-07-12 17:27:18 +000019#include <algorithm>
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +000020#include <map>
Leonard Mosescu2ae3ec32018-07-12 17:27:18 +000021#include <vector>
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000022
23using namespace lldb_private;
24using namespace minidump;
25
26llvm::Optional<MinidumpParser>
Kate Stoneb9c1b512016-09-06 20:57:50 +000027MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp) {
28 if (data_buf_sp->GetByteSize() < sizeof(MinidumpHeader)) {
29 return llvm::None;
30 }
Leonard Mosescu2ae3ec32018-07-12 17:27:18 +000031 return MinidumpParser(data_buf_sp);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000032}
33
Leonard Mosescu2ae3ec32018-07-12 17:27:18 +000034MinidumpParser::MinidumpParser(const lldb::DataBufferSP &data_buf_sp)
35 : m_data_sp(data_buf_sp) {}
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000036
Dimitar Vlahovski4c319072016-09-27 19:05:55 +000037llvm::ArrayRef<uint8_t> MinidumpParser::GetData() {
Dimitar Vlahovski36e21a32016-10-05 18:11:45 +000038 return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(),
39 m_data_sp->GetByteSize());
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000040}
41
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +000042llvm::ArrayRef<uint8_t>
Kate Stoneb9c1b512016-09-06 20:57:50 +000043MinidumpParser::GetStream(MinidumpStreamType stream_type) {
44 auto iter = m_directory_map.find(static_cast<uint32_t>(stream_type));
45 if (iter == m_directory_map.end())
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +000046 return {};
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000047
Kate Stoneb9c1b512016-09-06 20:57:50 +000048 // check if there is enough data
Dimitar Vlahovski36e21a32016-10-05 18:11:45 +000049 if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize())
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +000050 return {};
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000051
Dimitar Vlahovski36e21a32016-10-05 18:11:45 +000052 return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes() + iter->second.rva,
53 iter->second.data_size);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000054}
55
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +000056llvm::Optional<std::string> MinidumpParser::GetMinidumpString(uint32_t rva) {
Dimitar Vlahovski36e21a32016-10-05 18:11:45 +000057 auto arr_ref = m_data_sp->GetData();
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +000058 if (rva > arr_ref.size())
59 return llvm::None;
60 arr_ref = arr_ref.drop_front(rva);
61 return parseMinidumpString(arr_ref);
62}
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +000063
Leonard Mosescu9fecd372018-05-02 20:06:17 +000064UUID MinidumpParser::GetModuleUUID(const MinidumpModule *module) {
65 auto cv_record =
66 GetData().slice(module->CV_record.rva, module->CV_record.data_size);
67
68 // Read the CV record signature
69 const llvm::support::ulittle32_t *signature = nullptr;
70 Status error = consumeObject(cv_record, signature);
71 if (error.Fail())
72 return UUID();
73
74 const CvSignature cv_signature =
75 static_cast<CvSignature>(static_cast<const uint32_t>(*signature));
76
77 if (cv_signature == CvSignature::Pdb70) {
78 // PDB70 record
79 const CvRecordPdb70 *pdb70_uuid = nullptr;
80 Status error = consumeObject(cv_record, pdb70_uuid);
Greg Clayton77c57202018-08-29 20:34:08 +000081 if (!error.Fail()) {
82 auto arch = GetArchitecture();
83 // For Apple targets we only need a 16 byte UUID so that we can match
84 // the UUID in the Module to actual UUIDs from the built binaries. The
85 // "Age" field is zero in breakpad minidump files for Apple targets, so
86 // we restrict the UUID to the "Uuid" field so we have a UUID we can use
87 // to match.
88 if (arch.GetTriple().getVendor() == llvm::Triple::Apple)
89 return UUID::fromData(pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid));
90 else
91 return UUID::fromData(pdb70_uuid, sizeof(*pdb70_uuid));
92 }
Pavel Labath2f93fd12018-06-26 15:12:20 +000093 } else if (cv_signature == CvSignature::ElfBuildId)
94 return UUID::fromData(cv_record);
Leonard Mosescu9fecd372018-05-02 20:06:17 +000095
96 return UUID();
97}
98
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +000099llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() {
100 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ThreadList);
101
102 if (data.size() == 0)
Kate Stoneb9c1b512016-09-06 20:57:50 +0000103 return llvm::None;
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000104
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000105 return MinidumpThread::ParseThreadList(data);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000106}
107
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000108llvm::ArrayRef<uint8_t>
109MinidumpParser::GetThreadContext(const MinidumpThread &td) {
110 if (td.thread_context.rva + td.thread_context.data_size > GetData().size())
Dimitar Vlahovski7b18dd42016-10-31 15:35:18 +0000111 return {};
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000112
113 return GetData().slice(td.thread_context.rva, td.thread_context.data_size);
114}
115
Dimitar Vlahovski7b18dd42016-10-31 15:35:18 +0000116llvm::ArrayRef<uint8_t>
117MinidumpParser::GetThreadContextWow64(const MinidumpThread &td) {
Adrian Prantl05097242018-04-30 16:49:04 +0000118 // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If
119 // the minidump was captured with a 64-bit debugger, then the CONTEXT we just
120 // grabbed from the mini_dump_thread is the one for the 64-bit "native"
121 // process rather than the 32-bit "guest" process we care about. In this
122 // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment
123 // Block) of the 64-bit process.
Dimitar Vlahovski7b18dd42016-10-31 15:35:18 +0000124 auto teb_mem = GetMemory(td.teb, sizeof(TEB64));
125 if (teb_mem.empty())
126 return {};
127
128 const TEB64 *wow64teb;
Zachary Turner97206d52017-05-12 04:51:55 +0000129 Status error = consumeObject(teb_mem, wow64teb);
Dimitar Vlahovski7b18dd42016-10-31 15:35:18 +0000130 if (error.Fail())
131 return {};
132
Adrian Prantl05097242018-04-30 16:49:04 +0000133 // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure
134 // that includes the 32-bit CONTEXT (after a ULONG). See:
135 // https://msdn.microsoft.com/en-us/library/ms681670.aspx
Dimitar Vlahovski7b18dd42016-10-31 15:35:18 +0000136 auto context =
137 GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32));
138 if (context.size() < sizeof(MinidumpContext_x86_32))
139 return {};
140
141 return context;
142 // NOTE: We don't currently use the TEB for anything else. If we
143 // need it in the future, the 32-bit TEB is located according to the address
144 // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]).
145}
146
Kate Stoneb9c1b512016-09-06 20:57:50 +0000147const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000148 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000149
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000150 if (data.size() == 0)
Kate Stoneb9c1b512016-09-06 20:57:50 +0000151 return nullptr;
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000152
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000153 return MinidumpSystemInfo::Parse(data);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000154}
155
Pavel Labath659cee52018-08-03 08:47:22 +0000156ArchSpec MinidumpParser::GetArchitecture() {
Greg Clayton19c8f392018-08-06 16:56:10 +0000157 if (m_arch.IsValid())
158 return m_arch;
159
160 // Set the architecture in m_arch
Pavel Labath659cee52018-08-03 08:47:22 +0000161 const MinidumpSystemInfo *system_info = GetSystemInfo();
162
163 if (!system_info)
Greg Clayton19c8f392018-08-06 16:56:10 +0000164 return m_arch;
Pavel Labath659cee52018-08-03 08:47:22 +0000165
166 // TODO what to do about big endiand flavors of arm ?
167 // TODO set the arm subarch stuff if the minidump has info about it
168
169 llvm::Triple triple;
170 triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
171
172 const MinidumpCPUArchitecture arch =
173 static_cast<const MinidumpCPUArchitecture>(
174 static_cast<const uint32_t>(system_info->processor_arch));
175
176 switch (arch) {
177 case MinidumpCPUArchitecture::X86:
178 triple.setArch(llvm::Triple::ArchType::x86);
179 break;
180 case MinidumpCPUArchitecture::AMD64:
181 triple.setArch(llvm::Triple::ArchType::x86_64);
182 break;
183 case MinidumpCPUArchitecture::ARM:
184 triple.setArch(llvm::Triple::ArchType::arm);
185 break;
186 case MinidumpCPUArchitecture::ARM64:
187 triple.setArch(llvm::Triple::ArchType::aarch64);
188 break;
189 default:
190 triple.setArch(llvm::Triple::ArchType::UnknownArch);
191 break;
192 }
193
194 const MinidumpOSPlatform os = static_cast<const MinidumpOSPlatform>(
195 static_cast<const uint32_t>(system_info->platform_id));
196
197 // TODO add all of the OSes that Minidump/breakpad distinguishes?
198 switch (os) {
199 case MinidumpOSPlatform::Win32S:
200 case MinidumpOSPlatform::Win32Windows:
201 case MinidumpOSPlatform::Win32NT:
202 case MinidumpOSPlatform::Win32CE:
203 triple.setOS(llvm::Triple::OSType::Win32);
204 break;
205 case MinidumpOSPlatform::Linux:
206 triple.setOS(llvm::Triple::OSType::Linux);
207 break;
208 case MinidumpOSPlatform::MacOSX:
209 triple.setOS(llvm::Triple::OSType::MacOSX);
Greg Clayton19c8f392018-08-06 16:56:10 +0000210 triple.setVendor(llvm::Triple::Apple);
211 break;
212 case MinidumpOSPlatform::IOS:
213 triple.setOS(llvm::Triple::OSType::IOS);
214 triple.setVendor(llvm::Triple::Apple);
Pavel Labath659cee52018-08-03 08:47:22 +0000215 break;
216 case MinidumpOSPlatform::Android:
217 triple.setOS(llvm::Triple::OSType::Linux);
218 triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
219 break;
Greg Clayton19c8f392018-08-06 16:56:10 +0000220 default: {
Pavel Labath659cee52018-08-03 08:47:22 +0000221 triple.setOS(llvm::Triple::OSType::UnknownOS);
Greg Clayton19c8f392018-08-06 16:56:10 +0000222 std::string csd_version;
223 if (auto s = GetMinidumpString(system_info->csd_version_rva))
224 csd_version = *s;
225 if (csd_version.find("Linux") != std::string::npos)
226 triple.setOS(llvm::Triple::OSType::Linux);
Pavel Labath659cee52018-08-03 08:47:22 +0000227 break;
Greg Clayton19c8f392018-08-06 16:56:10 +0000228 }
Pavel Labath659cee52018-08-03 08:47:22 +0000229 }
Greg Clayton19c8f392018-08-06 16:56:10 +0000230 m_arch.SetTriple(triple);
231 return m_arch;
Pavel Labath659cee52018-08-03 08:47:22 +0000232}
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000233
Kate Stoneb9c1b512016-09-06 20:57:50 +0000234const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000235 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MiscInfo);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000236
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000237 if (data.size() == 0)
Kate Stoneb9c1b512016-09-06 20:57:50 +0000238 return nullptr;
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000239
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000240 return MinidumpMiscInfo::Parse(data);
241}
242
243llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
244 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxProcStatus);
245
246 if (data.size() == 0)
247 return llvm::None;
248
249 return LinuxProcStatus::Parse(data);
250}
251
252llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
253 const MinidumpMiscInfo *misc_info = GetMiscInfo();
254 if (misc_info != nullptr) {
255 return misc_info->GetPid();
256 }
257
258 llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
259 if (proc_status.hasValue()) {
260 return proc_status->GetPid();
261 }
262
263 return llvm::None;
264}
265
266llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() {
267 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ModuleList);
268
269 if (data.size() == 0)
270 return {};
271
272 return MinidumpModule::ParseModuleList(data);
273}
274
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000275std::vector<const MinidumpModule *> MinidumpParser::GetFilteredModuleList() {
276 llvm::ArrayRef<MinidumpModule> modules = GetModuleList();
Greg Claytone55979b2018-12-13 17:24:30 +0000277 // map module_name -> filtered_modules index
278 typedef llvm::StringMap<size_t> MapType;
279 MapType module_name_to_filtered_index;
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000280
281 std::vector<const MinidumpModule *> filtered_modules;
Greg Claytone55979b2018-12-13 17:24:30 +0000282
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000283 llvm::Optional<std::string> name;
284 std::string module_name;
285
286 for (const auto &module : modules) {
287 name = GetMinidumpString(module.module_name_rva);
Greg Claytone55979b2018-12-13 17:24:30 +0000288
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000289 if (!name)
290 continue;
Greg Claytone55979b2018-12-13 17:24:30 +0000291
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000292 module_name = name.getValue();
Greg Claytone55979b2018-12-13 17:24:30 +0000293
294 MapType::iterator iter;
295 bool inserted;
296 // See if we have inserted this module aready into filtered_modules. If we
297 // haven't insert an entry into module_name_to_filtered_index with the
298 // index where we will insert it if it isn't in the vector already.
299 std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace(
300 module_name, filtered_modules.size());
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000301
Greg Claytone55979b2018-12-13 17:24:30 +0000302 if (inserted) {
303 // This module has not been seen yet, insert it into filtered_modules at
304 // the index that was inserted into module_name_to_filtered_index using
305 // "filtered_modules.size()" above.
306 filtered_modules.push_back(&module);
307 } else {
308 // This module has been seen. Modules are sometimes mentioned multiple
309 // times when they are mapped discontiguously, so find the module with
310 // the lowest "base_of_image" and use that as the filtered module.
311 auto dup_module = filtered_modules[iter->second];
312 if (module.base_of_image < dup_module->base_of_image)
313 filtered_modules[iter->second] = &module;
314 }
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000315 }
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000316 return filtered_modules;
317}
318
Dimitar Vlahovski1d2859e2016-09-13 15:54:38 +0000319const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
320 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
321
322 if (data.size() == 0)
323 return nullptr;
324
325 return MinidumpExceptionStream::Parse(data);
Dimitar Vlahovski2e50d8e2016-09-01 11:29:53 +0000326}
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000327
328llvm::Optional<minidump::Range>
329MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
330 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList);
331 llvm::ArrayRef<uint8_t> data64 = GetStream(MinidumpStreamType::Memory64List);
332
333 if (data.empty() && data64.empty())
334 return llvm::None;
335
336 if (!data.empty()) {
337 llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list =
338 MinidumpMemoryDescriptor::ParseMemoryList(data);
339
340 if (memory_list.empty())
341 return llvm::None;
342
343 for (const auto &memory_desc : memory_list) {
344 const MinidumpLocationDescriptor &loc_desc = memory_desc.memory;
345 const lldb::addr_t range_start = memory_desc.start_of_memory_range;
346 const size_t range_size = loc_desc.data_size;
347
348 if (loc_desc.rva + loc_desc.data_size > GetData().size())
349 return llvm::None;
350
351 if (range_start <= addr && addr < range_start + range_size) {
352 return minidump::Range(range_start,
353 GetData().slice(loc_desc.rva, range_size));
354 }
355 }
356 }
357
Adrian Prantl05097242018-04-30 16:49:04 +0000358 // Some Minidumps have a Memory64ListStream that captures all the heap memory
359 // (full-memory Minidumps). We can't exactly use the same loop as above,
360 // because the Minidump uses slightly different data structures to describe
361 // those
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000362
363 if (!data64.empty()) {
364 llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
365 uint64_t base_rva;
366 std::tie(memory64_list, base_rva) =
367 MinidumpMemoryDescriptor64::ParseMemory64List(data64);
368
369 if (memory64_list.empty())
370 return llvm::None;
371
372 for (const auto &memory_desc64 : memory64_list) {
373 const lldb::addr_t range_start = memory_desc64.start_of_memory_range;
374 const size_t range_size = memory_desc64.data_size;
375
376 if (base_rva + range_size > GetData().size())
377 return llvm::None;
378
379 if (range_start <= addr && addr < range_start + range_size) {
380 return minidump::Range(range_start,
381 GetData().slice(base_rva, range_size));
382 }
383 base_rva += range_size;
384 }
385 }
386
387 return llvm::None;
388}
389
390llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
391 size_t size) {
392 // I don't have a sense of how frequently this is called or how many memory
393 // ranges a Minidump typically has, so I'm not sure if searching for the
394 // appropriate range linearly each time is stupid. Perhaps we should build
395 // an index for faster lookups.
396 llvm::Optional<minidump::Range> range = FindMemoryRange(addr);
397 if (!range)
398 return {};
399
400 // There's at least some overlap between the beginning of the desired range
Adrian Prantl05097242018-04-30 16:49:04 +0000401 // (addr) and the current range. Figure out where the overlap begins and how
402 // much overlap there is.
Dimitar Vlahovskib52206d2016-10-19 14:14:18 +0000403
404 const size_t offset = addr - range->start;
405
406 if (addr < range->start || offset >= range->range_ref.size())
407 return {};
408
409 const size_t overlap = std::min(size, range->range_ref.size() - offset);
410 return range->range_ref.slice(offset, overlap);
411}
412
413llvm::Optional<MemoryRegionInfo>
414MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
415 MemoryRegionInfo info;
416 llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryInfoList);
417 if (data.empty())
418 return llvm::None;
419
420 std::vector<const MinidumpMemoryInfo *> mem_info_list =
421 MinidumpMemoryInfo::ParseMemoryInfoList(data);
422 if (mem_info_list.empty())
423 return llvm::None;
424
425 const auto yes = MemoryRegionInfo::eYes;
426 const auto no = MemoryRegionInfo::eNo;
427
428 const MinidumpMemoryInfo *next_entry = nullptr;
429 for (const auto &entry : mem_info_list) {
430 const auto head = entry->base_address;
431 const auto tail = head + entry->region_size;
432
433 if (head <= load_addr && load_addr < tail) {
434 info.GetRange().SetRangeBase(
435 (entry->state != uint32_t(MinidumpMemoryInfoState::MemFree))
436 ? head
437 : load_addr);
438 info.GetRange().SetRangeEnd(tail);
439
440 const uint32_t PageNoAccess =
441 static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageNoAccess);
442 info.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no);
443
444 const uint32_t PageWritable =
445 static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageWritable);
446 info.SetWritable((entry->protect & PageWritable) != 0 ? yes : no);
447
448 const uint32_t PageExecutable = static_cast<uint32_t>(
449 MinidumpMemoryProtectionContants::PageExecutable);
450 info.SetExecutable((entry->protect & PageExecutable) != 0 ? yes : no);
451
452 const uint32_t MemFree =
453 static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree);
454 info.SetMapped((entry->state != MemFree) ? yes : no);
455
456 return info;
457 } else if (head > load_addr &&
458 (next_entry == nullptr || head < next_entry->base_address)) {
459 // In case there is no region containing load_addr keep track of the
460 // nearest region after load_addr so we can return the distance to it.
461 next_entry = entry;
462 }
463 }
464
465 // No containing region found. Create an unmapped region that extends to the
466 // next region or LLDB_INVALID_ADDRESS
467 info.GetRange().SetRangeBase(load_addr);
468 info.GetRange().SetRangeEnd((next_entry != nullptr) ? next_entry->base_address
469 : LLDB_INVALID_ADDRESS);
470 info.SetReadable(no);
471 info.SetWritable(no);
472 info.SetExecutable(no);
473 info.SetMapped(no);
474
475 // Note that the memory info list doesn't seem to contain ranges in kernel
476 // space, so if you're walking a stack that has kernel frames, the stack may
477 // appear truncated.
478 return info;
479}
Leonard Mosescu2ae3ec32018-07-12 17:27:18 +0000480
481Status MinidumpParser::Initialize() {
482 Status error;
483
484 lldbassert(m_directory_map.empty());
485
486 llvm::ArrayRef<uint8_t> header_data(m_data_sp->GetBytes(),
487 sizeof(MinidumpHeader));
488 const MinidumpHeader *header = MinidumpHeader::Parse(header_data);
489 if (header == nullptr) {
490 error.SetErrorString("invalid minidump: can't parse the header");
491 return error;
492 }
493
494 // A minidump without at least one stream is clearly ill-formed
495 if (header->streams_count == 0) {
496 error.SetErrorString("invalid minidump: no streams present");
497 return error;
498 }
499
500 struct FileRange {
501 uint32_t offset = 0;
502 uint32_t size = 0;
503
504 FileRange(uint32_t offset, uint32_t size) : offset(offset), size(size) {}
505 uint32_t end() const { return offset + size; }
506 };
507
508 const uint32_t file_size = m_data_sp->GetByteSize();
509
510 // Build a global minidump file map, checking for:
511 // - overlapping streams/data structures
512 // - truncation (streams pointing past the end of file)
513 std::vector<FileRange> minidump_map;
514
515 // Add the minidump header to the file map
516 if (sizeof(MinidumpHeader) > file_size) {
517 error.SetErrorString("invalid minidump: truncated header");
518 return error;
519 }
520 minidump_map.emplace_back( 0, sizeof(MinidumpHeader) );
521
522 // Add the directory entries to the file map
523 FileRange directory_range(header->stream_directory_rva,
524 header->streams_count *
525 sizeof(MinidumpDirectory));
526 if (directory_range.end() > file_size) {
527 error.SetErrorString("invalid minidump: truncated streams directory");
528 return error;
529 }
530 minidump_map.push_back(directory_range);
531
532 // Parse stream directory entries
533 llvm::ArrayRef<uint8_t> directory_data(
534 m_data_sp->GetBytes() + directory_range.offset, directory_range.size);
535 for (uint32_t i = 0; i < header->streams_count; ++i) {
536 const MinidumpDirectory *directory_entry = nullptr;
537 error = consumeObject(directory_data, directory_entry);
538 if (error.Fail())
539 return error;
540 if (directory_entry->stream_type == 0) {
541 // Ignore dummy streams (technically ill-formed, but a number of
542 // existing minidumps seem to contain such streams)
543 if (directory_entry->location.data_size == 0)
544 continue;
545 error.SetErrorString("invalid minidump: bad stream type");
546 return error;
547 }
548 // Update the streams map, checking for duplicate stream types
549 if (!m_directory_map
550 .insert({directory_entry->stream_type, directory_entry->location})
551 .second) {
552 error.SetErrorString("invalid minidump: duplicate stream type");
553 return error;
554 }
555 // Ignore the zero-length streams for layout checks
556 if (directory_entry->location.data_size != 0) {
557 minidump_map.emplace_back(directory_entry->location.rva,
558 directory_entry->location.data_size);
559 }
560 }
561
562 // Sort the file map ranges by start offset
563 std::sort(minidump_map.begin(), minidump_map.end(),
564 [](const FileRange &a, const FileRange &b) {
565 return a.offset < b.offset;
566 });
567
568 // Check for overlapping streams/data structures
569 for (size_t i = 1; i < minidump_map.size(); ++i) {
570 const auto &prev_range = minidump_map[i - 1];
571 if (prev_range.end() > minidump_map[i].offset) {
572 error.SetErrorString("invalid minidump: overlapping streams");
573 return error;
574 }
575 }
576
577 // Check for streams past the end of file
578 const auto &last_range = minidump_map.back();
579 if (last_range.end() > file_size) {
580 error.SetErrorString("invalid minidump: truncated stream");
581 return error;
582 }
583
584 return error;
585}