//===- Minidump.cpp - Minidump object file implementation -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Object/Minidump.h" #include "llvm/Support/ConvertUTF.h" using namespace llvm; using namespace llvm::object; using namespace llvm::minidump; std::optional> MinidumpFile::getRawStream(minidump::StreamType Type) const { auto It = StreamMap.find(Type); if (It != StreamMap.end()) return getRawStream(Streams[It->second]); return std::nullopt; } Expected MinidumpFile::getString(size_t Offset) const { // Minidump strings consist of a 32-bit length field, which gives the size of // the string in *bytes*. This is followed by the actual string encoded in // UTF16. auto ExpectedSize = getDataSliceAs(getData(), Offset, 1); if (!ExpectedSize) return ExpectedSize.takeError(); size_t Size = (*ExpectedSize)[0]; if (Size % 2 != 0) return createError("String size not even"); Size /= 2; if (Size == 0) return ""; Offset += sizeof(support::ulittle32_t); auto ExpectedData = getDataSliceAs(getData(), Offset, Size); if (!ExpectedData) return ExpectedData.takeError(); SmallVector WStr(Size); copy(*ExpectedData, WStr.begin()); std::string Result; if (!convertUTF16ToUTF8String(WStr, Result)) return createError("String decoding failed"); return Result; } iterator_range MinidumpFile::getExceptionStreams() const { return make_range(ExceptionStreamsIterator(ExceptionStreams, this), ExceptionStreamsIterator({}, this)); } Expected> MinidumpFile::getMemoryInfoList() const { std::optional> Stream = getRawStream(StreamType::MemoryInfoList); if (!Stream) return createError("No such stream"); auto ExpectedHeader = getDataSliceAs(*Stream, 0, 1); if (!ExpectedHeader) return ExpectedHeader.takeError(); const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0]; Expected> Data = getDataSlice(*Stream, H.SizeOfHeader, H.SizeOfEntry * H.NumberOfEntries); if (!Data) return Data.takeError(); return make_range(MemoryInfoIterator(*Data, H.SizeOfEntry), MemoryInfoIterator({}, H.SizeOfEntry)); } Expected> MinidumpFile::getDataSlice(ArrayRef Data, uint64_t Offset, uint64_t Size) { // Check for overflow. if (Offset + Size < Offset || Offset + Size < Size || Offset + Size > Data.size()) return createEOFError(); return Data.slice(Offset, Size); } Expected> MinidumpFile::create(MemoryBufferRef Source) { ArrayRef Data = arrayRefFromStringRef(Source.getBuffer()); auto ExpectedHeader = getDataSliceAs(Data, 0, 1); if (!ExpectedHeader) return ExpectedHeader.takeError(); const minidump::Header &Hdr = (*ExpectedHeader)[0]; if (Hdr.Signature != Header::MagicSignature) return createError("Invalid signature"); if ((Hdr.Version & 0xffff) != Header::MagicVersion) return createError("Invalid version"); auto ExpectedStreams = getDataSliceAs(Data, Hdr.StreamDirectoryRVA, Hdr.NumberOfStreams); if (!ExpectedStreams) return ExpectedStreams.takeError(); DenseMap StreamMap; std::vector ExceptionStreams; for (const auto &StreamDescriptor : llvm::enumerate(*ExpectedStreams)) { StreamType Type = StreamDescriptor.value().Type; const LocationDescriptor &Loc = StreamDescriptor.value().Location; Expected> Stream = getDataSlice(Data, Loc.RVA, Loc.DataSize); if (!Stream) return Stream.takeError(); if (Type == StreamType::Unused && Loc.DataSize == 0) { // Ignore dummy streams. This is technically ill-formed, but a number of // existing minidumps seem to contain such streams. continue; } // Exceptions can be treated as a special case of streams. Other streams // represent a list of entities, but exceptions are unique per stream. if (Type == StreamType::Exception) { ExceptionStreams.push_back(StreamDescriptor.value()); continue; } if (Type == DenseMapInfo::getEmptyKey() || Type == DenseMapInfo::getTombstoneKey()) return createError("Cannot handle one of the minidump streams"); // Update the directory map, checking for duplicate stream types. if (!StreamMap.try_emplace(Type, StreamDescriptor.index()).second) return createError("Duplicate stream type"); } return std::unique_ptr( new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap), std::move(ExceptionStreams))); } iterator_range MinidumpFile::getMemory64List(Error &Err) const { ErrorAsOutParameter ErrAsOutParam(Err); auto end = FallibleMemory64Iterator::end(Memory64Iterator::end()); Expected ListHeader = getMemoryList64Header(); if (!ListHeader) { Err = ListHeader.takeError(); return make_range(end, end); } std::optional> Stream = getRawStream(StreamType::Memory64List); if (!Stream) { Err = createError("No such stream"); return make_range(end, end); } Expected> Descriptors = getDataSliceAs( *Stream, sizeof(Memory64ListHeader), ListHeader->NumberOfMemoryRanges); if (!Descriptors) { Err = Descriptors.takeError(); return make_range(end, end); } if (!Descriptors->empty() && ListHeader->BaseRVA + Descriptors->front().DataSize > getData().size()) { Err = createError("Memory64List header RVA out of range"); return make_range(end, end); } return make_range(FallibleMemory64Iterator::itr( Memory64Iterator::begin( getData().slice(ListHeader->BaseRVA), *Descriptors), Err), FallibleMemory64Iterator::end(Memory64Iterator::end())); }