1 //===- Minidump.cpp - Minidump object file implementation -----------------===// 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 "llvm/Object/Minidump.h" 10 #include "llvm/Support/ConvertUTF.h" 11 12 using namespace llvm; 13 using namespace llvm::object; 14 using namespace llvm::minidump; 15 16 std::optional<ArrayRef<uint8_t>> 17 MinidumpFile::getRawStream(minidump::StreamType Type) const { 18 auto It = StreamMap.find(Type); 19 if (It != StreamMap.end()) 20 return getRawStream(Streams[It->second]); 21 return std::nullopt; 22 } 23 24 Expected<std::string> MinidumpFile::getString(size_t Offset) const { 25 // Minidump strings consist of a 32-bit length field, which gives the size of 26 // the string in *bytes*. This is followed by the actual string encoded in 27 // UTF16. 28 auto ExpectedSize = 29 getDataSliceAs<support::ulittle32_t>(getData(), Offset, 1); 30 if (!ExpectedSize) 31 return ExpectedSize.takeError(); 32 size_t Size = (*ExpectedSize)[0]; 33 if (Size % 2 != 0) 34 return createError("String size not even"); 35 Size /= 2; 36 if (Size == 0) 37 return ""; 38 39 Offset += sizeof(support::ulittle32_t); 40 auto ExpectedData = 41 getDataSliceAs<support::ulittle16_t>(getData(), Offset, Size); 42 if (!ExpectedData) 43 return ExpectedData.takeError(); 44 45 SmallVector<UTF16, 32> WStr(Size); 46 copy(*ExpectedData, WStr.begin()); 47 48 std::string Result; 49 if (!convertUTF16ToUTF8String(WStr, Result)) 50 return createError("String decoding failed"); 51 52 return Result; 53 } 54 55 iterator_range<llvm::object::MinidumpFile::ExceptionStreamsIterator> 56 MinidumpFile::getExceptionStreams() const { 57 return make_range(ExceptionStreamsIterator(ExceptionStreams, this), 58 ExceptionStreamsIterator({}, this)); 59 } 60 61 Expected<iterator_range<MinidumpFile::MemoryInfoIterator>> 62 MinidumpFile::getMemoryInfoList() const { 63 std::optional<ArrayRef<uint8_t>> Stream = 64 getRawStream(StreamType::MemoryInfoList); 65 if (!Stream) 66 return createError("No such stream"); 67 auto ExpectedHeader = 68 getDataSliceAs<minidump::MemoryInfoListHeader>(*Stream, 0, 1); 69 if (!ExpectedHeader) 70 return ExpectedHeader.takeError(); 71 const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0]; 72 Expected<ArrayRef<uint8_t>> Data = 73 getDataSlice(*Stream, H.SizeOfHeader, H.SizeOfEntry * H.NumberOfEntries); 74 if (!Data) 75 return Data.takeError(); 76 return make_range(MemoryInfoIterator(*Data, H.SizeOfEntry), 77 MemoryInfoIterator({}, H.SizeOfEntry)); 78 } 79 80 Expected<ArrayRef<uint8_t>> MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, 81 uint64_t Offset, 82 uint64_t Size) { 83 // Check for overflow. 84 if (Offset + Size < Offset || Offset + Size < Size || 85 Offset + Size > Data.size()) 86 return createEOFError(); 87 return Data.slice(Offset, Size); 88 } 89 90 Expected<std::unique_ptr<MinidumpFile>> 91 MinidumpFile::create(MemoryBufferRef Source) { 92 ArrayRef<uint8_t> Data = arrayRefFromStringRef(Source.getBuffer()); 93 auto ExpectedHeader = getDataSliceAs<minidump::Header>(Data, 0, 1); 94 if (!ExpectedHeader) 95 return ExpectedHeader.takeError(); 96 97 const minidump::Header &Hdr = (*ExpectedHeader)[0]; 98 if (Hdr.Signature != Header::MagicSignature) 99 return createError("Invalid signature"); 100 if ((Hdr.Version & 0xffff) != Header::MagicVersion) 101 return createError("Invalid version"); 102 103 auto ExpectedStreams = getDataSliceAs<Directory>(Data, Hdr.StreamDirectoryRVA, 104 Hdr.NumberOfStreams); 105 if (!ExpectedStreams) 106 return ExpectedStreams.takeError(); 107 108 DenseMap<StreamType, std::size_t> StreamMap; 109 std::vector<Directory> ExceptionStreams; 110 for (const auto &StreamDescriptor : llvm::enumerate(*ExpectedStreams)) { 111 StreamType Type = StreamDescriptor.value().Type; 112 const LocationDescriptor &Loc = StreamDescriptor.value().Location; 113 114 Expected<ArrayRef<uint8_t>> Stream = 115 getDataSlice(Data, Loc.RVA, Loc.DataSize); 116 if (!Stream) 117 return Stream.takeError(); 118 119 if (Type == StreamType::Unused && Loc.DataSize == 0) { 120 // Ignore dummy streams. This is technically ill-formed, but a number of 121 // existing minidumps seem to contain such streams. 122 continue; 123 } 124 125 // Exceptions can be treated as a special case of streams. Other streams 126 // represent a list of entities, but exceptions are unique per stream. 127 if (Type == StreamType::Exception) { 128 ExceptionStreams.push_back(StreamDescriptor.value()); 129 continue; 130 } 131 132 if (Type == DenseMapInfo<StreamType>::getEmptyKey() || 133 Type == DenseMapInfo<StreamType>::getTombstoneKey()) 134 return createError("Cannot handle one of the minidump streams"); 135 136 // Update the directory map, checking for duplicate stream types. 137 if (!StreamMap.try_emplace(Type, StreamDescriptor.index()).second) 138 return createError("Duplicate stream type"); 139 } 140 141 return std::unique_ptr<MinidumpFile>( 142 new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap), 143 std::move(ExceptionStreams))); 144 } 145 146 iterator_range<MinidumpFile::FallibleMemory64Iterator> 147 MinidumpFile::getMemory64List(Error &Err) const { 148 ErrorAsOutParameter ErrAsOutParam(Err); 149 auto end = FallibleMemory64Iterator::end(Memory64Iterator::end()); 150 Expected<minidump::Memory64ListHeader> ListHeader = getMemoryList64Header(); 151 if (!ListHeader) { 152 Err = ListHeader.takeError(); 153 return make_range(end, end); 154 } 155 156 std::optional<ArrayRef<uint8_t>> Stream = 157 getRawStream(StreamType::Memory64List); 158 if (!Stream) { 159 Err = createError("No such stream"); 160 return make_range(end, end); 161 } 162 163 Expected<ArrayRef<minidump::MemoryDescriptor_64>> Descriptors = 164 getDataSliceAs<minidump::MemoryDescriptor_64>( 165 *Stream, sizeof(Memory64ListHeader), 166 ListHeader->NumberOfMemoryRanges); 167 168 if (!Descriptors) { 169 Err = Descriptors.takeError(); 170 return make_range(end, end); 171 } 172 173 if (!Descriptors->empty() && 174 ListHeader->BaseRVA + Descriptors->front().DataSize > getData().size()) { 175 Err = createError("Memory64List header RVA out of range"); 176 return make_range(end, end); 177 } 178 179 return make_range(FallibleMemory64Iterator::itr( 180 Memory64Iterator::begin( 181 getData().slice(ListHeader->BaseRVA), *Descriptors), 182 Err), 183 FallibleMemory64Iterator::end(Memory64Iterator::end())); 184 } 185