1351278Sdim//===- Minidump.cpp - Minidump object file implementation -----------------===// 2351278Sdim// 3351278Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4351278Sdim// See https://llvm.org/LICENSE.txt for license information. 5351278Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6351278Sdim// 7351278Sdim//===----------------------------------------------------------------------===// 8351278Sdim 9351278Sdim#include "llvm/Object/Minidump.h" 10351278Sdim#include "llvm/Object/Error.h" 11351278Sdim#include "llvm/Support/ConvertUTF.h" 12351278Sdim 13351278Sdimusing namespace llvm; 14351278Sdimusing namespace llvm::object; 15351278Sdimusing namespace llvm::minidump; 16351278Sdim 17351278SdimOptional<ArrayRef<uint8_t>> 18351278SdimMinidumpFile::getRawStream(minidump::StreamType Type) const { 19351278Sdim auto It = StreamMap.find(Type); 20351278Sdim if (It != StreamMap.end()) 21351278Sdim return getRawStream(Streams[It->second]); 22351278Sdim return None; 23351278Sdim} 24351278Sdim 25351278SdimExpected<std::string> MinidumpFile::getString(size_t Offset) const { 26351278Sdim // Minidump strings consist of a 32-bit length field, which gives the size of 27351278Sdim // the string in *bytes*. This is followed by the actual string encoded in 28351278Sdim // UTF16. 29351278Sdim auto ExpectedSize = 30351278Sdim getDataSliceAs<support::ulittle32_t>(getData(), Offset, 1); 31351278Sdim if (!ExpectedSize) 32351278Sdim return ExpectedSize.takeError(); 33351278Sdim size_t Size = (*ExpectedSize)[0]; 34351278Sdim if (Size % 2 != 0) 35351278Sdim return createError("String size not even"); 36351278Sdim Size /= 2; 37351278Sdim if (Size == 0) 38351278Sdim return ""; 39351278Sdim 40351278Sdim Offset += sizeof(support::ulittle32_t); 41351278Sdim auto ExpectedData = 42351278Sdim getDataSliceAs<support::ulittle16_t>(getData(), Offset, Size); 43351278Sdim if (!ExpectedData) 44351278Sdim return ExpectedData.takeError(); 45351278Sdim 46351278Sdim SmallVector<UTF16, 32> WStr(Size); 47351278Sdim copy(*ExpectedData, WStr.begin()); 48351278Sdim 49351278Sdim std::string Result; 50351278Sdim if (!convertUTF16ToUTF8String(WStr, Result)) 51351278Sdim return createError("String decoding failed"); 52351278Sdim 53351278Sdim return Result; 54351278Sdim} 55351278Sdim 56360784SdimExpected<iterator_range<MinidumpFile::MemoryInfoIterator>> 57360784SdimMinidumpFile::getMemoryInfoList() const { 58360784Sdim Optional<ArrayRef<uint8_t>> Stream = getRawStream(StreamType::MemoryInfoList); 59360784Sdim if (!Stream) 60360784Sdim return createError("No such stream"); 61360784Sdim auto ExpectedHeader = 62360784Sdim getDataSliceAs<minidump::MemoryInfoListHeader>(*Stream, 0, 1); 63360784Sdim if (!ExpectedHeader) 64360784Sdim return ExpectedHeader.takeError(); 65360784Sdim const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0]; 66360784Sdim Expected<ArrayRef<uint8_t>> Data = 67360784Sdim getDataSlice(*Stream, H.SizeOfHeader, H.SizeOfEntry * H.NumberOfEntries); 68360784Sdim if (!Data) 69360784Sdim return Data.takeError(); 70360784Sdim return make_range(MemoryInfoIterator(*Data, H.SizeOfEntry), 71360784Sdim MemoryInfoIterator({}, H.SizeOfEntry)); 72360784Sdim} 73360784Sdim 74351278Sdimtemplate <typename T> 75360784SdimExpected<ArrayRef<T>> MinidumpFile::getListStream(StreamType Type) const { 76360784Sdim Optional<ArrayRef<uint8_t>> Stream = getRawStream(Type); 77360784Sdim if (!Stream) 78351278Sdim return createError("No such stream"); 79360784Sdim auto ExpectedSize = getDataSliceAs<support::ulittle32_t>(*Stream, 0, 1); 80351278Sdim if (!ExpectedSize) 81351278Sdim return ExpectedSize.takeError(); 82351278Sdim 83351278Sdim size_t ListSize = ExpectedSize.get()[0]; 84351278Sdim 85351278Sdim size_t ListOffset = 4; 86351278Sdim // Some producers insert additional padding bytes to align the list to an 87351278Sdim // 8-byte boundary. Check for that by comparing the list size with the overall 88351278Sdim // stream size. 89360784Sdim if (ListOffset + sizeof(T) * ListSize < Stream->size()) 90351278Sdim ListOffset = 8; 91351278Sdim 92360784Sdim return getDataSliceAs<T>(*Stream, ListOffset, ListSize); 93351278Sdim} 94351278Sdimtemplate Expected<ArrayRef<Module>> 95351278Sdim MinidumpFile::getListStream(StreamType) const; 96351278Sdimtemplate Expected<ArrayRef<Thread>> 97351278Sdim MinidumpFile::getListStream(StreamType) const; 98351278Sdimtemplate Expected<ArrayRef<MemoryDescriptor>> 99351278Sdim MinidumpFile::getListStream(StreamType) const; 100351278Sdim 101351278SdimExpected<ArrayRef<uint8_t>> 102351278SdimMinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) { 103351278Sdim // Check for overflow. 104351278Sdim if (Offset + Size < Offset || Offset + Size < Size || 105351278Sdim Offset + Size > Data.size()) 106351278Sdim return createEOFError(); 107351278Sdim return Data.slice(Offset, Size); 108351278Sdim} 109351278Sdim 110351278SdimExpected<std::unique_ptr<MinidumpFile>> 111351278SdimMinidumpFile::create(MemoryBufferRef Source) { 112351278Sdim ArrayRef<uint8_t> Data = arrayRefFromStringRef(Source.getBuffer()); 113351278Sdim auto ExpectedHeader = getDataSliceAs<minidump::Header>(Data, 0, 1); 114351278Sdim if (!ExpectedHeader) 115351278Sdim return ExpectedHeader.takeError(); 116351278Sdim 117351278Sdim const minidump::Header &Hdr = (*ExpectedHeader)[0]; 118351278Sdim if (Hdr.Signature != Header::MagicSignature) 119351278Sdim return createError("Invalid signature"); 120351278Sdim if ((Hdr.Version & 0xffff) != Header::MagicVersion) 121351278Sdim return createError("Invalid version"); 122351278Sdim 123351278Sdim auto ExpectedStreams = getDataSliceAs<Directory>(Data, Hdr.StreamDirectoryRVA, 124351278Sdim Hdr.NumberOfStreams); 125351278Sdim if (!ExpectedStreams) 126351278Sdim return ExpectedStreams.takeError(); 127351278Sdim 128351278Sdim DenseMap<StreamType, std::size_t> StreamMap; 129360784Sdim for (const auto &StreamDescriptor : llvm::enumerate(*ExpectedStreams)) { 130360784Sdim StreamType Type = StreamDescriptor.value().Type; 131360784Sdim const LocationDescriptor &Loc = StreamDescriptor.value().Location; 132351278Sdim 133360784Sdim Expected<ArrayRef<uint8_t>> Stream = 134360784Sdim getDataSlice(Data, Loc.RVA, Loc.DataSize); 135360784Sdim if (!Stream) 136360784Sdim return Stream.takeError(); 137351278Sdim 138351278Sdim if (Type == StreamType::Unused && Loc.DataSize == 0) { 139351278Sdim // Ignore dummy streams. This is technically ill-formed, but a number of 140351278Sdim // existing minidumps seem to contain such streams. 141351278Sdim continue; 142351278Sdim } 143351278Sdim 144351278Sdim if (Type == DenseMapInfo<StreamType>::getEmptyKey() || 145351278Sdim Type == DenseMapInfo<StreamType>::getTombstoneKey()) 146351278Sdim return createError("Cannot handle one of the minidump streams"); 147351278Sdim 148351278Sdim // Update the directory map, checking for duplicate stream types. 149360784Sdim if (!StreamMap.try_emplace(Type, StreamDescriptor.index()).second) 150351278Sdim return createError("Duplicate stream type"); 151351278Sdim } 152351278Sdim 153351278Sdim return std::unique_ptr<MinidumpFile>( 154351278Sdim new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap))); 155351278Sdim} 156