1 //===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
2 //
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 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "MinidumpParser.h"
11 #include "NtStructures.h"
12 #include "RegisterContextMinidump_x86_32.h"
13 
14 #include "lldb/Target/MemoryRegionInfo.h"
15 #include "lldb/Utility/LLDBAssert.h"
16 #include "Plugins/Process/Utility/LinuxProcMaps.h"
17 
18 // C includes
19 // C++ includes
20 #include <algorithm>
21 #include <map>
22 #include <vector>
23 #include <utility>
24 
25 using namespace lldb_private;
26 using namespace minidump;
27 
28 llvm::Optional<MinidumpParser>
29 MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp) {
30   if (data_buf_sp->GetByteSize() < sizeof(MinidumpHeader)) {
31     return llvm::None;
32   }
33   return MinidumpParser(data_buf_sp);
34 }
35 
36 MinidumpParser::MinidumpParser(const lldb::DataBufferSP &data_buf_sp)
37     : m_data_sp(data_buf_sp) {}
38 
39 llvm::ArrayRef<uint8_t> MinidumpParser::GetData() {
40   return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(),
41                                  m_data_sp->GetByteSize());
42 }
43 
44 llvm::ArrayRef<uint8_t>
45 MinidumpParser::GetStream(MinidumpStreamType stream_type) {
46   auto iter = m_directory_map.find(static_cast<uint32_t>(stream_type));
47   if (iter == m_directory_map.end())
48     return {};
49 
50   // check if there is enough data
51   if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize())
52     return {};
53 
54   return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes() + iter->second.rva,
55                                  iter->second.data_size);
56 }
57 
58 llvm::Optional<std::string> MinidumpParser::GetMinidumpString(uint32_t rva) {
59   auto arr_ref = m_data_sp->GetData();
60   if (rva > arr_ref.size())
61     return llvm::None;
62   arr_ref = arr_ref.drop_front(rva);
63   return parseMinidumpString(arr_ref);
64 }
65 
66 UUID MinidumpParser::GetModuleUUID(const MinidumpModule *module) {
67   auto cv_record =
68       GetData().slice(module->CV_record.rva, module->CV_record.data_size);
69 
70   // Read the CV record signature
71   const llvm::support::ulittle32_t *signature = nullptr;
72   Status error = consumeObject(cv_record, signature);
73   if (error.Fail())
74     return UUID();
75 
76   const CvSignature cv_signature =
77       static_cast<CvSignature>(static_cast<const uint32_t>(*signature));
78 
79   if (cv_signature == CvSignature::Pdb70) {
80     // PDB70 record
81     const CvRecordPdb70 *pdb70_uuid = nullptr;
82     Status error = consumeObject(cv_record, pdb70_uuid);
83     if (!error.Fail()) {
84       auto arch = GetArchitecture();
85       // For Apple targets we only need a 16 byte UUID so that we can match
86       // the UUID in the Module to actual UUIDs from the built binaries. The
87       // "Age" field is zero in breakpad minidump files for Apple targets, so
88       // we restrict the UUID to the "Uuid" field so we have a UUID we can use
89       // to match.
90       if (arch.GetTriple().getVendor() == llvm::Triple::Apple)
91         return UUID::fromData(pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid));
92       else
93         return UUID::fromData(pdb70_uuid, sizeof(*pdb70_uuid));
94     }
95   } else if (cv_signature == CvSignature::ElfBuildId)
96     return UUID::fromData(cv_record);
97 
98   return UUID();
99 }
100 
101 llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() {
102   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ThreadList);
103 
104   if (data.size() == 0)
105     return llvm::None;
106 
107   return MinidumpThread::ParseThreadList(data);
108 }
109 
110 llvm::ArrayRef<uint8_t>
111 MinidumpParser::GetThreadContext(const MinidumpThread &td) {
112   if (td.thread_context.rva + td.thread_context.data_size > GetData().size())
113     return {};
114 
115   return GetData().slice(td.thread_context.rva, td.thread_context.data_size);
116 }
117 
118 llvm::ArrayRef<uint8_t>
119 MinidumpParser::GetThreadContextWow64(const MinidumpThread &td) {
120   // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If
121   // the minidump was captured with a 64-bit debugger, then the CONTEXT we just
122   // grabbed from the mini_dump_thread is the one for the 64-bit "native"
123   // process rather than the 32-bit "guest" process we care about.  In this
124   // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment
125   // Block) of the 64-bit process.
126   auto teb_mem = GetMemory(td.teb, sizeof(TEB64));
127   if (teb_mem.empty())
128     return {};
129 
130   const TEB64 *wow64teb;
131   Status error = consumeObject(teb_mem, wow64teb);
132   if (error.Fail())
133     return {};
134 
135   // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure
136   // that includes the 32-bit CONTEXT (after a ULONG). See:
137   // https://msdn.microsoft.com/en-us/library/ms681670.aspx
138   auto context =
139       GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32));
140   if (context.size() < sizeof(MinidumpContext_x86_32))
141     return {};
142 
143   return context;
144   // NOTE:  We don't currently use the TEB for anything else.  If we
145   // need it in the future, the 32-bit TEB is located according to the address
146   // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]).
147 }
148 
149 const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
150   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
151 
152   if (data.size() == 0)
153     return nullptr;
154 
155   return MinidumpSystemInfo::Parse(data);
156 }
157 
158 ArchSpec MinidumpParser::GetArchitecture() {
159   if (m_arch.IsValid())
160     return m_arch;
161 
162   // Set the architecture in m_arch
163   const MinidumpSystemInfo *system_info = GetSystemInfo();
164 
165   if (!system_info)
166     return m_arch;
167 
168   // TODO what to do about big endiand flavors of arm ?
169   // TODO set the arm subarch stuff if the minidump has info about it
170 
171   llvm::Triple triple;
172   triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
173 
174   const MinidumpCPUArchitecture arch =
175       static_cast<const MinidumpCPUArchitecture>(
176           static_cast<const uint32_t>(system_info->processor_arch));
177 
178   switch (arch) {
179   case MinidumpCPUArchitecture::X86:
180     triple.setArch(llvm::Triple::ArchType::x86);
181     break;
182   case MinidumpCPUArchitecture::AMD64:
183     triple.setArch(llvm::Triple::ArchType::x86_64);
184     break;
185   case MinidumpCPUArchitecture::ARM:
186     triple.setArch(llvm::Triple::ArchType::arm);
187     break;
188   case MinidumpCPUArchitecture::ARM64:
189     triple.setArch(llvm::Triple::ArchType::aarch64);
190     break;
191   default:
192     triple.setArch(llvm::Triple::ArchType::UnknownArch);
193     break;
194   }
195 
196   const MinidumpOSPlatform os = static_cast<const MinidumpOSPlatform>(
197       static_cast<const uint32_t>(system_info->platform_id));
198 
199   // TODO add all of the OSes that Minidump/breakpad distinguishes?
200   switch (os) {
201   case MinidumpOSPlatform::Win32S:
202   case MinidumpOSPlatform::Win32Windows:
203   case MinidumpOSPlatform::Win32NT:
204   case MinidumpOSPlatform::Win32CE:
205     triple.setOS(llvm::Triple::OSType::Win32);
206     break;
207   case MinidumpOSPlatform::Linux:
208     triple.setOS(llvm::Triple::OSType::Linux);
209     break;
210   case MinidumpOSPlatform::MacOSX:
211     triple.setOS(llvm::Triple::OSType::MacOSX);
212     triple.setVendor(llvm::Triple::Apple);
213     break;
214   case MinidumpOSPlatform::IOS:
215     triple.setOS(llvm::Triple::OSType::IOS);
216     triple.setVendor(llvm::Triple::Apple);
217     break;
218   case MinidumpOSPlatform::Android:
219     triple.setOS(llvm::Triple::OSType::Linux);
220     triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
221     break;
222   default: {
223     triple.setOS(llvm::Triple::OSType::UnknownOS);
224     std::string csd_version;
225     if (auto s = GetMinidumpString(system_info->csd_version_rva))
226       csd_version = *s;
227     if (csd_version.find("Linux") != std::string::npos)
228       triple.setOS(llvm::Triple::OSType::Linux);
229     break;
230     }
231   }
232   m_arch.SetTriple(triple);
233   return m_arch;
234 }
235 
236 const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
237   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MiscInfo);
238 
239   if (data.size() == 0)
240     return nullptr;
241 
242   return MinidumpMiscInfo::Parse(data);
243 }
244 
245 llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
246   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxProcStatus);
247 
248   if (data.size() == 0)
249     return llvm::None;
250 
251   return LinuxProcStatus::Parse(data);
252 }
253 
254 llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
255   const MinidumpMiscInfo *misc_info = GetMiscInfo();
256   if (misc_info != nullptr) {
257     return misc_info->GetPid();
258   }
259 
260   llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
261   if (proc_status.hasValue()) {
262     return proc_status->GetPid();
263   }
264 
265   return llvm::None;
266 }
267 
268 llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() {
269   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ModuleList);
270 
271   if (data.size() == 0)
272     return {};
273 
274   return MinidumpModule::ParseModuleList(data);
275 }
276 
277 std::vector<const MinidumpModule *> MinidumpParser::GetFilteredModuleList() {
278   llvm::ArrayRef<MinidumpModule> modules = GetModuleList();
279   // map module_name -> filtered_modules index
280   typedef llvm::StringMap<size_t> MapType;
281   MapType module_name_to_filtered_index;
282 
283   std::vector<const MinidumpModule *> filtered_modules;
284 
285   llvm::Optional<std::string> name;
286   std::string module_name;
287 
288   for (const auto &module : modules) {
289     name = GetMinidumpString(module.module_name_rva);
290 
291     if (!name)
292       continue;
293 
294     module_name = name.getValue();
295 
296     MapType::iterator iter;
297     bool inserted;
298     // See if we have inserted this module aready into filtered_modules. If we
299     // haven't insert an entry into module_name_to_filtered_index with the
300     // index where we will insert it if it isn't in the vector already.
301     std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace(
302         module_name, filtered_modules.size());
303 
304     if (inserted) {
305       // This module has not been seen yet, insert it into filtered_modules at
306       // the index that was inserted into module_name_to_filtered_index using
307       // "filtered_modules.size()" above.
308       filtered_modules.push_back(&module);
309     } else {
310       // This module has been seen. Modules are sometimes mentioned multiple
311       // times when they are mapped discontiguously, so find the module with
312       // the lowest "base_of_image" and use that as the filtered module.
313       auto dup_module = filtered_modules[iter->second];
314       if (module.base_of_image < dup_module->base_of_image)
315         filtered_modules[iter->second] = &module;
316     }
317   }
318   return filtered_modules;
319 }
320 
321 const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
322   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
323 
324   if (data.size() == 0)
325     return nullptr;
326 
327   return MinidumpExceptionStream::Parse(data);
328 }
329 
330 llvm::Optional<minidump::Range>
331 MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
332   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList);
333   llvm::ArrayRef<uint8_t> data64 = GetStream(MinidumpStreamType::Memory64List);
334 
335   if (data.empty() && data64.empty())
336     return llvm::None;
337 
338   if (!data.empty()) {
339     llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list =
340         MinidumpMemoryDescriptor::ParseMemoryList(data);
341 
342     if (memory_list.empty())
343       return llvm::None;
344 
345     for (const auto &memory_desc : memory_list) {
346       const MinidumpLocationDescriptor &loc_desc = memory_desc.memory;
347       const lldb::addr_t range_start = memory_desc.start_of_memory_range;
348       const size_t range_size = loc_desc.data_size;
349 
350       if (loc_desc.rva + loc_desc.data_size > GetData().size())
351         return llvm::None;
352 
353       if (range_start <= addr && addr < range_start + range_size) {
354         return minidump::Range(range_start,
355                                GetData().slice(loc_desc.rva, range_size));
356       }
357     }
358   }
359 
360   // Some Minidumps have a Memory64ListStream that captures all the heap memory
361   // (full-memory Minidumps).  We can't exactly use the same loop as above,
362   // because the Minidump uses slightly different data structures to describe
363   // those
364 
365   if (!data64.empty()) {
366     llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
367     uint64_t base_rva;
368     std::tie(memory64_list, base_rva) =
369         MinidumpMemoryDescriptor64::ParseMemory64List(data64);
370 
371     if (memory64_list.empty())
372       return llvm::None;
373 
374     for (const auto &memory_desc64 : memory64_list) {
375       const lldb::addr_t range_start = memory_desc64.start_of_memory_range;
376       const size_t range_size = memory_desc64.data_size;
377 
378       if (base_rva + range_size > GetData().size())
379         return llvm::None;
380 
381       if (range_start <= addr && addr < range_start + range_size) {
382         return minidump::Range(range_start,
383                                GetData().slice(base_rva, range_size));
384       }
385       base_rva += range_size;
386     }
387   }
388 
389   return llvm::None;
390 }
391 
392 llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
393                                                   size_t size) {
394   // I don't have a sense of how frequently this is called or how many memory
395   // ranges a Minidump typically has, so I'm not sure if searching for the
396   // appropriate range linearly each time is stupid.  Perhaps we should build
397   // an index for faster lookups.
398   llvm::Optional<minidump::Range> range = FindMemoryRange(addr);
399   if (!range)
400     return {};
401 
402   // There's at least some overlap between the beginning of the desired range
403   // (addr) and the current range.  Figure out where the overlap begins and how
404   // much overlap there is.
405 
406   const size_t offset = addr - range->start;
407 
408   if (addr < range->start || offset >= range->range_ref.size())
409     return {};
410 
411   const size_t overlap = std::min(size, range->range_ref.size() - offset);
412   return range->range_ref.slice(offset, overlap);
413 }
414 
415 static bool
416 CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser,
417                                 std::vector<MemoryRegionInfo> &regions) {
418   auto data = parser.GetStream(MinidumpStreamType::LinuxMaps);
419   if (data.empty())
420     return false;
421   ParseLinuxMapRegions(llvm::toStringRef(data),
422                        [&](const lldb_private::MemoryRegionInfo &region,
423                            const lldb_private::Status &status) -> bool {
424     if (status.Success())
425       regions.push_back(region);
426     return true;
427   });
428   return !regions.empty();
429 }
430 
431 static bool
432 CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
433                                      std::vector<MemoryRegionInfo> &regions) {
434   auto data = parser.GetStream(MinidumpStreamType::MemoryInfoList);
435   if (data.empty())
436     return false;
437   auto mem_info_list = MinidumpMemoryInfo::ParseMemoryInfoList(data);
438   if (mem_info_list.empty())
439     return false;
440   constexpr auto yes = MemoryRegionInfo::eYes;
441   constexpr auto no = MemoryRegionInfo::eNo;
442   regions.reserve(mem_info_list.size());
443   for (const auto &entry : mem_info_list) {
444     MemoryRegionInfo region;
445     region.GetRange().SetRangeBase(entry->base_address);
446     region.GetRange().SetByteSize(entry->region_size);
447     region.SetReadable(entry->isReadable() ? yes : no);
448     region.SetWritable(entry->isWritable() ? yes : no);
449     region.SetExecutable(entry->isExecutable() ? yes : no);
450     region.SetMapped(entry->isMapped() ? yes : no);
451     regions.push_back(region);
452   }
453   return !regions.empty();
454 }
455 
456 static bool
457 CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
458                                  std::vector<MemoryRegionInfo> &regions) {
459   auto data = parser.GetStream(MinidumpStreamType::MemoryList);
460   if (data.empty())
461     return false;
462   auto memory_list = MinidumpMemoryDescriptor::ParseMemoryList(data);
463   if (memory_list.empty())
464     return false;
465   regions.reserve(memory_list.size());
466   for (const auto &memory_desc : memory_list) {
467     if (memory_desc.memory.data_size == 0)
468       continue;
469     MemoryRegionInfo region;
470     region.GetRange().SetRangeBase(memory_desc.start_of_memory_range);
471     region.GetRange().SetByteSize(memory_desc.memory.data_size);
472     region.SetReadable(MemoryRegionInfo::eYes);
473     region.SetMapped(MemoryRegionInfo::eYes);
474     regions.push_back(region);
475   }
476   regions.shrink_to_fit();
477   return !regions.empty();
478 }
479 
480 static bool
481 CreateRegionsCacheFromMemory64List(MinidumpParser &parser,
482                                    std::vector<MemoryRegionInfo> &regions) {
483   llvm::ArrayRef<uint8_t> data =
484       parser.GetStream(MinidumpStreamType::Memory64List);
485   if (data.empty())
486     return false;
487   llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
488   uint64_t base_rva;
489   std::tie(memory64_list, base_rva) =
490       MinidumpMemoryDescriptor64::ParseMemory64List(data);
491 
492   if (memory64_list.empty())
493     return false;
494 
495   regions.reserve(memory64_list.size());
496   for (const auto &memory_desc : memory64_list) {
497     if (memory_desc.data_size == 0)
498       continue;
499     MemoryRegionInfo region;
500     region.GetRange().SetRangeBase(memory_desc.start_of_memory_range);
501     region.GetRange().SetByteSize(memory_desc.data_size);
502     region.SetReadable(MemoryRegionInfo::eYes);
503     region.SetMapped(MemoryRegionInfo::eYes);
504     regions.push_back(region);
505   }
506   regions.shrink_to_fit();
507   return !regions.empty();
508 }
509 
510 MemoryRegionInfo
511 MinidumpParser::FindMemoryRegion(lldb::addr_t load_addr) const {
512   auto begin = m_regions.begin();
513   auto end = m_regions.end();
514   auto pos = std::lower_bound(begin, end, load_addr);
515   if (pos != end && pos->GetRange().Contains(load_addr))
516     return *pos;
517 
518   MemoryRegionInfo region;
519   if (pos == begin)
520     region.GetRange().SetRangeBase(0);
521   else {
522     auto prev = pos - 1;
523     if (prev->GetRange().Contains(load_addr))
524       return *prev;
525     region.GetRange().SetRangeBase(prev->GetRange().GetRangeEnd());
526   }
527   if (pos == end)
528     region.GetRange().SetRangeEnd(UINT64_MAX);
529   else
530     region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase());
531   region.SetReadable(MemoryRegionInfo::eNo);
532   region.SetWritable(MemoryRegionInfo::eNo);
533   region.SetExecutable(MemoryRegionInfo::eNo);
534   region.SetMapped(MemoryRegionInfo::eNo);
535   return region;
536 }
537 
538 MemoryRegionInfo
539 MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
540   if (!m_parsed_regions) {
541     m_parsed_regions = true;
542     // We haven't cached our memory regions yet we will create the region cache
543     // once. We create the region cache using the best source. We start with
544     // the linux maps since they are the most complete and have names for the
545     // regions. Next we try the MemoryInfoList since it has
546     // read/write/execute/map data, and then fall back to the MemoryList and
547     // Memory64List to just get a list of the memory that is mapped in this
548     // core file
549     if (!CreateRegionsCacheFromLinuxMaps(*this, m_regions))
550       if (!CreateRegionsCacheFromMemoryInfoList(*this, m_regions))
551         if (!CreateRegionsCacheFromMemoryList(*this, m_regions))
552           CreateRegionsCacheFromMemory64List(*this, m_regions);
553     std::sort(m_regions.begin(), m_regions.end());
554   }
555   return FindMemoryRegion(load_addr);
556 }
557 
558 Status MinidumpParser::Initialize() {
559   Status error;
560 
561   lldbassert(m_directory_map.empty());
562 
563   llvm::ArrayRef<uint8_t> header_data(m_data_sp->GetBytes(),
564                                       sizeof(MinidumpHeader));
565   const MinidumpHeader *header = MinidumpHeader::Parse(header_data);
566   if (header == nullptr) {
567     error.SetErrorString("invalid minidump: can't parse the header");
568     return error;
569   }
570 
571   // A minidump without at least one stream is clearly ill-formed
572   if (header->streams_count == 0) {
573     error.SetErrorString("invalid minidump: no streams present");
574     return error;
575   }
576 
577   struct FileRange {
578     uint32_t offset = 0;
579     uint32_t size = 0;
580 
581     FileRange(uint32_t offset, uint32_t size) : offset(offset), size(size) {}
582     uint32_t end() const { return offset + size; }
583   };
584 
585   const uint32_t file_size = m_data_sp->GetByteSize();
586 
587   // Build a global minidump file map, checking for:
588   // - overlapping streams/data structures
589   // - truncation (streams pointing past the end of file)
590   std::vector<FileRange> minidump_map;
591 
592   // Add the minidump header to the file map
593   if (sizeof(MinidumpHeader) > file_size) {
594     error.SetErrorString("invalid minidump: truncated header");
595     return error;
596   }
597   minidump_map.emplace_back( 0, sizeof(MinidumpHeader) );
598 
599   // Add the directory entries to the file map
600   FileRange directory_range(header->stream_directory_rva,
601                             header->streams_count *
602                                 sizeof(MinidumpDirectory));
603   if (directory_range.end() > file_size) {
604     error.SetErrorString("invalid minidump: truncated streams directory");
605     return error;
606   }
607   minidump_map.push_back(directory_range);
608 
609   // Parse stream directory entries
610   llvm::ArrayRef<uint8_t> directory_data(
611       m_data_sp->GetBytes() + directory_range.offset, directory_range.size);
612   for (uint32_t i = 0; i < header->streams_count; ++i) {
613     const MinidumpDirectory *directory_entry = nullptr;
614     error = consumeObject(directory_data, directory_entry);
615     if (error.Fail())
616       return error;
617     if (directory_entry->stream_type == 0) {
618       // Ignore dummy streams (technically ill-formed, but a number of
619       // existing minidumps seem to contain such streams)
620       if (directory_entry->location.data_size == 0)
621         continue;
622       error.SetErrorString("invalid minidump: bad stream type");
623       return error;
624     }
625     // Update the streams map, checking for duplicate stream types
626     if (!m_directory_map
627              .insert({directory_entry->stream_type, directory_entry->location})
628              .second) {
629       error.SetErrorString("invalid minidump: duplicate stream type");
630       return error;
631     }
632     // Ignore the zero-length streams for layout checks
633     if (directory_entry->location.data_size != 0) {
634       minidump_map.emplace_back(directory_entry->location.rva,
635                                 directory_entry->location.data_size);
636     }
637   }
638 
639   // Sort the file map ranges by start offset
640   std::sort(minidump_map.begin(), minidump_map.end(),
641             [](const FileRange &a, const FileRange &b) {
642               return a.offset < b.offset;
643             });
644 
645   // Check for overlapping streams/data structures
646   for (size_t i = 1; i < minidump_map.size(); ++i) {
647     const auto &prev_range = minidump_map[i - 1];
648     if (prev_range.end() > minidump_map[i].offset) {
649       error.SetErrorString("invalid minidump: overlapping streams");
650       return error;
651     }
652   }
653 
654   // Check for streams past the end of file
655   const auto &last_range = minidump_map.back();
656   if (last_range.end() > file_size) {
657     error.SetErrorString("invalid minidump: truncated stream");
658     return error;
659   }
660 
661   return error;
662 }
663 
664 #define ENUM_TO_CSTR(ST) case (uint32_t)MinidumpStreamType::ST: return #ST
665 
666 llvm::StringRef
667 MinidumpParser::GetStreamTypeAsString(uint32_t stream_type) {
668   switch (stream_type) {
669     ENUM_TO_CSTR(Unused);
670     ENUM_TO_CSTR(Reserved0);
671     ENUM_TO_CSTR(Reserved1);
672     ENUM_TO_CSTR(ThreadList);
673     ENUM_TO_CSTR(ModuleList);
674     ENUM_TO_CSTR(MemoryList);
675     ENUM_TO_CSTR(Exception);
676     ENUM_TO_CSTR(SystemInfo);
677     ENUM_TO_CSTR(ThreadExList);
678     ENUM_TO_CSTR(Memory64List);
679     ENUM_TO_CSTR(CommentA);
680     ENUM_TO_CSTR(CommentW);
681     ENUM_TO_CSTR(HandleData);
682     ENUM_TO_CSTR(FunctionTable);
683     ENUM_TO_CSTR(UnloadedModuleList);
684     ENUM_TO_CSTR(MiscInfo);
685     ENUM_TO_CSTR(MemoryInfoList);
686     ENUM_TO_CSTR(ThreadInfoList);
687     ENUM_TO_CSTR(HandleOperationList);
688     ENUM_TO_CSTR(Token);
689     ENUM_TO_CSTR(JavascriptData);
690     ENUM_TO_CSTR(SystemMemoryInfo);
691     ENUM_TO_CSTR(ProcessVMCounters);
692     ENUM_TO_CSTR(BreakpadInfo);
693     ENUM_TO_CSTR(AssertionInfo);
694     ENUM_TO_CSTR(LinuxCPUInfo);
695     ENUM_TO_CSTR(LinuxProcStatus);
696     ENUM_TO_CSTR(LinuxLSBRelease);
697     ENUM_TO_CSTR(LinuxCMDLine);
698     ENUM_TO_CSTR(LinuxEnviron);
699     ENUM_TO_CSTR(LinuxAuxv);
700     ENUM_TO_CSTR(LinuxMaps);
701     ENUM_TO_CSTR(LinuxDSODebug);
702     ENUM_TO_CSTR(LinuxProcStat);
703     ENUM_TO_CSTR(LinuxProcUptime);
704     ENUM_TO_CSTR(LinuxProcFD);
705   }
706   return "unknown stream type";
707 }
708