Path: blob/main/contrib/llvm-project/llvm/lib/Object/Minidump.cpp
35233 views
//===- Minidump.cpp - Minidump object file implementation -----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/Object/Minidump.h"9#include "llvm/Object/Error.h"10#include "llvm/Support/ConvertUTF.h"1112using namespace llvm;13using namespace llvm::object;14using namespace llvm::minidump;1516std::optional<ArrayRef<uint8_t>>17MinidumpFile::getRawStream(minidump::StreamType Type) const {18auto It = StreamMap.find(Type);19if (It != StreamMap.end())20return getRawStream(Streams[It->second]);21return std::nullopt;22}2324Expected<std::string> MinidumpFile::getString(size_t Offset) const {25// Minidump strings consist of a 32-bit length field, which gives the size of26// the string in *bytes*. This is followed by the actual string encoded in27// UTF16.28auto ExpectedSize =29getDataSliceAs<support::ulittle32_t>(getData(), Offset, 1);30if (!ExpectedSize)31return ExpectedSize.takeError();32size_t Size = (*ExpectedSize)[0];33if (Size % 2 != 0)34return createError("String size not even");35Size /= 2;36if (Size == 0)37return "";3839Offset += sizeof(support::ulittle32_t);40auto ExpectedData =41getDataSliceAs<support::ulittle16_t>(getData(), Offset, Size);42if (!ExpectedData)43return ExpectedData.takeError();4445SmallVector<UTF16, 32> WStr(Size);46copy(*ExpectedData, WStr.begin());4748std::string Result;49if (!convertUTF16ToUTF8String(WStr, Result))50return createError("String decoding failed");5152return Result;53}5455Expected<iterator_range<MinidumpFile::MemoryInfoIterator>>56MinidumpFile::getMemoryInfoList() const {57std::optional<ArrayRef<uint8_t>> Stream =58getRawStream(StreamType::MemoryInfoList);59if (!Stream)60return createError("No such stream");61auto ExpectedHeader =62getDataSliceAs<minidump::MemoryInfoListHeader>(*Stream, 0, 1);63if (!ExpectedHeader)64return ExpectedHeader.takeError();65const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0];66Expected<ArrayRef<uint8_t>> Data =67getDataSlice(*Stream, H.SizeOfHeader, H.SizeOfEntry * H.NumberOfEntries);68if (!Data)69return Data.takeError();70return make_range(MemoryInfoIterator(*Data, H.SizeOfEntry),71MemoryInfoIterator({}, H.SizeOfEntry));72}7374template <typename T>75Expected<ArrayRef<T>> MinidumpFile::getListStream(StreamType Type) const {76std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type);77if (!Stream)78return createError("No such stream");79auto ExpectedSize = getDataSliceAs<support::ulittle32_t>(*Stream, 0, 1);80if (!ExpectedSize)81return ExpectedSize.takeError();8283size_t ListSize = ExpectedSize.get()[0];8485size_t ListOffset = 4;86// Some producers insert additional padding bytes to align the list to an87// 8-byte boundary. Check for that by comparing the list size with the overall88// stream size.89if (ListOffset + sizeof(T) * ListSize < Stream->size())90ListOffset = 8;9192return getDataSliceAs<T>(*Stream, ListOffset, ListSize);93}94template Expected<ArrayRef<Module>>95MinidumpFile::getListStream(StreamType) const;96template Expected<ArrayRef<Thread>>97MinidumpFile::getListStream(StreamType) const;98template Expected<ArrayRef<MemoryDescriptor>>99MinidumpFile::getListStream(StreamType) const;100101Expected<ArrayRef<uint8_t>>102MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) {103// Check for overflow.104if (Offset + Size < Offset || Offset + Size < Size ||105Offset + Size > Data.size())106return createEOFError();107return Data.slice(Offset, Size);108}109110Expected<std::unique_ptr<MinidumpFile>>111MinidumpFile::create(MemoryBufferRef Source) {112ArrayRef<uint8_t> Data = arrayRefFromStringRef(Source.getBuffer());113auto ExpectedHeader = getDataSliceAs<minidump::Header>(Data, 0, 1);114if (!ExpectedHeader)115return ExpectedHeader.takeError();116117const minidump::Header &Hdr = (*ExpectedHeader)[0];118if (Hdr.Signature != Header::MagicSignature)119return createError("Invalid signature");120if ((Hdr.Version & 0xffff) != Header::MagicVersion)121return createError("Invalid version");122123auto ExpectedStreams = getDataSliceAs<Directory>(Data, Hdr.StreamDirectoryRVA,124Hdr.NumberOfStreams);125if (!ExpectedStreams)126return ExpectedStreams.takeError();127128DenseMap<StreamType, std::size_t> StreamMap;129for (const auto &StreamDescriptor : llvm::enumerate(*ExpectedStreams)) {130StreamType Type = StreamDescriptor.value().Type;131const LocationDescriptor &Loc = StreamDescriptor.value().Location;132133Expected<ArrayRef<uint8_t>> Stream =134getDataSlice(Data, Loc.RVA, Loc.DataSize);135if (!Stream)136return Stream.takeError();137138if (Type == StreamType::Unused && Loc.DataSize == 0) {139// Ignore dummy streams. This is technically ill-formed, but a number of140// existing minidumps seem to contain such streams.141continue;142}143144if (Type == DenseMapInfo<StreamType>::getEmptyKey() ||145Type == DenseMapInfo<StreamType>::getTombstoneKey())146return createError("Cannot handle one of the minidump streams");147148// Update the directory map, checking for duplicate stream types.149if (!StreamMap.try_emplace(Type, StreamDescriptor.index()).second)150return createError("Duplicate stream type");151}152153return std::unique_ptr<MinidumpFile>(154new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap)));155}156157158