1 //===- Minidump.h - Minidump object file implementation ---------*- C++ -*-===// 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 #ifndef LLVM_OBJECT_MINIDUMP_H 10 #define LLVM_OBJECT_MINIDUMP_H 11 12 #include "llvm/ADT/DenseMap.h" 13 #include "llvm/ADT/StringExtras.h" 14 #include "llvm/ADT/iterator.h" 15 #include "llvm/BinaryFormat/Minidump.h" 16 #include "llvm/Object/Binary.h" 17 #include "llvm/Support/Error.h" 18 19 namespace llvm { 20 namespace object { 21 22 /// A class providing access to the contents of a minidump file. 23 class MinidumpFile : public Binary { 24 public: 25 /// Construct a new MinidumpFile object from the given memory buffer. Returns 26 /// an error if this file cannot be identified as a minidump file, or if its 27 /// contents are badly corrupted (i.e. we cannot read the stream directory). 28 static Expected<std::unique_ptr<MinidumpFile>> create(MemoryBufferRef Source); 29 30 static bool classof(const Binary *B) { return B->isMinidump(); } 31 32 /// Returns the contents of the minidump header. 33 const minidump::Header &header() const { return Header; } 34 35 /// Returns the list of streams (stream directory entries) in this file. 36 ArrayRef<minidump::Directory> streams() const { return Streams; } 37 38 /// Returns the raw contents of the stream given by the directory entry. 39 ArrayRef<uint8_t> getRawStream(const minidump::Directory &Stream) const { 40 return getData().slice(Stream.Location.RVA, Stream.Location.DataSize); 41 } 42 43 /// Returns the raw contents of the stream of the given type, or std::nullopt 44 /// if the file does not contain a stream of this type. 45 std::optional<ArrayRef<uint8_t>> 46 getRawStream(minidump::StreamType Type) const; 47 48 /// Returns the raw contents of an object given by the LocationDescriptor. An 49 /// error is returned if the descriptor points outside of the minidump file. 50 Expected<ArrayRef<uint8_t>> 51 getRawData(minidump::LocationDescriptor Desc) const { 52 return getDataSlice(getData(), Desc.RVA, Desc.DataSize); 53 } 54 55 /// Returns the minidump string at the given offset. An error is returned if 56 /// we fail to parse the string, or the string is invalid UTF16. 57 Expected<std::string> getString(size_t Offset) const; 58 59 /// Returns the contents of the SystemInfo stream, cast to the appropriate 60 /// type. An error is returned if the file does not contain this stream, or 61 /// the stream is smaller than the size of the SystemInfo structure. The 62 /// internal consistency of the stream is not checked in any way. 63 Expected<const minidump::SystemInfo &> getSystemInfo() const { 64 return getStream<minidump::SystemInfo>(minidump::StreamType::SystemInfo); 65 } 66 67 /// Returns the module list embedded in the ModuleList stream. An error is 68 /// returned if the file does not contain this stream, or if the stream is 69 /// not large enough to contain the number of modules declared in the stream 70 /// header. The consistency of the Module entries themselves is not checked in 71 /// any way. 72 Expected<ArrayRef<minidump::Module>> getModuleList() const { 73 return getListStream<minidump::Module>(minidump::StreamType::ModuleList); 74 } 75 76 /// Returns the thread list embedded in the ThreadList stream. An error is 77 /// returned if the file does not contain this stream, or if the stream is 78 /// not large enough to contain the number of threads declared in the stream 79 /// header. The consistency of the Thread entries themselves is not checked in 80 /// any way. 81 Expected<ArrayRef<minidump::Thread>> getThreadList() const { 82 return getListStream<minidump::Thread>(minidump::StreamType::ThreadList); 83 } 84 85 /// Returns the contents of the Exception stream. An error is returned if the 86 /// file does not contain this stream, or the stream is smaller than the size 87 /// of the ExceptionStream structure. The internal consistency of the stream 88 /// is not checked in any way. 89 Expected<const minidump::ExceptionStream &> getExceptionStream() const { 90 return getStream<minidump::ExceptionStream>( 91 minidump::StreamType::Exception); 92 } 93 94 /// Returns the list of descriptors embedded in the MemoryList stream. The 95 /// descriptors provide the content of interesting regions of memory at the 96 /// time the minidump was taken. An error is returned if the file does not 97 /// contain this stream, or if the stream is not large enough to contain the 98 /// number of memory descriptors declared in the stream header. The 99 /// consistency of the MemoryDescriptor entries themselves is not checked in 100 /// any way. 101 Expected<ArrayRef<minidump::MemoryDescriptor>> getMemoryList() const { 102 return getListStream<minidump::MemoryDescriptor>( 103 minidump::StreamType::MemoryList); 104 } 105 106 class MemoryInfoIterator 107 : public iterator_facade_base<MemoryInfoIterator, 108 std::forward_iterator_tag, 109 minidump::MemoryInfo> { 110 public: 111 MemoryInfoIterator(ArrayRef<uint8_t> Storage, size_t Stride) 112 : Storage(Storage), Stride(Stride) { 113 assert(Storage.size() % Stride == 0); 114 } 115 116 bool operator==(const MemoryInfoIterator &R) const { 117 return Storage.size() == R.Storage.size(); 118 } 119 120 const minidump::MemoryInfo &operator*() const { 121 assert(Storage.size() >= sizeof(minidump::MemoryInfo)); 122 return *reinterpret_cast<const minidump::MemoryInfo *>(Storage.data()); 123 } 124 125 MemoryInfoIterator &operator++() { 126 Storage = Storage.drop_front(Stride); 127 return *this; 128 } 129 130 private: 131 ArrayRef<uint8_t> Storage; 132 size_t Stride; 133 }; 134 135 /// Returns the list of descriptors embedded in the MemoryInfoList stream. The 136 /// descriptors provide properties (e.g. permissions) of interesting regions 137 /// of memory at the time the minidump was taken. An error is returned if the 138 /// file does not contain this stream, or if the stream is not large enough to 139 /// contain the number of memory descriptors declared in the stream header. 140 /// The consistency of the MemoryInfoList entries themselves is not checked 141 /// in any way. 142 Expected<iterator_range<MemoryInfoIterator>> getMemoryInfoList() const; 143 144 private: 145 static Error createError(StringRef Str) { 146 return make_error<GenericBinaryError>(Str, object_error::parse_failed); 147 } 148 149 static Error createEOFError() { 150 return make_error<GenericBinaryError>("Unexpected EOF", 151 object_error::unexpected_eof); 152 } 153 154 /// Return a slice of the given data array, with bounds checking. 155 static Expected<ArrayRef<uint8_t>> getDataSlice(ArrayRef<uint8_t> Data, 156 size_t Offset, size_t Size); 157 158 /// Return the slice of the given data array as an array of objects of the 159 /// given type. The function checks that the input array is large enough to 160 /// contain the correct number of objects of the given type. 161 template <typename T> 162 static Expected<ArrayRef<T>> getDataSliceAs(ArrayRef<uint8_t> Data, 163 size_t Offset, size_t Count); 164 165 MinidumpFile(MemoryBufferRef Source, const minidump::Header &Header, 166 ArrayRef<minidump::Directory> Streams, 167 DenseMap<minidump::StreamType, std::size_t> StreamMap) 168 : Binary(ID_Minidump, Source), Header(Header), Streams(Streams), 169 StreamMap(std::move(StreamMap)) {} 170 171 ArrayRef<uint8_t> getData() const { 172 return arrayRefFromStringRef(Data.getBuffer()); 173 } 174 175 /// Return the stream of the given type, cast to the appropriate type. Checks 176 /// that the stream is large enough to hold an object of this type. 177 template <typename T> 178 Expected<const T &> getStream(minidump::StreamType Stream) const; 179 180 /// Return the contents of a stream which contains a list of fixed-size items, 181 /// prefixed by the list size. 182 template <typename T> 183 Expected<ArrayRef<T>> getListStream(minidump::StreamType Stream) const; 184 185 const minidump::Header &Header; 186 ArrayRef<minidump::Directory> Streams; 187 DenseMap<minidump::StreamType, std::size_t> StreamMap; 188 }; 189 190 template <typename T> 191 Expected<const T &> MinidumpFile::getStream(minidump::StreamType Type) const { 192 if (std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type)) { 193 if (Stream->size() >= sizeof(T)) 194 return *reinterpret_cast<const T *>(Stream->data()); 195 return createEOFError(); 196 } 197 return createError("No such stream"); 198 } 199 200 template <typename T> 201 Expected<ArrayRef<T>> MinidumpFile::getDataSliceAs(ArrayRef<uint8_t> Data, 202 size_t Offset, 203 size_t Count) { 204 // Check for overflow. 205 if (Count > std::numeric_limits<size_t>::max() / sizeof(T)) 206 return createEOFError(); 207 Expected<ArrayRef<uint8_t>> Slice = 208 getDataSlice(Data, Offset, sizeof(T) * Count); 209 if (!Slice) 210 return Slice.takeError(); 211 return ArrayRef<T>(reinterpret_cast<const T *>(Slice->data()), Count); 212 } 213 214 } // end namespace object 215 } // end namespace llvm 216 217 #endif // LLVM_OBJECT_MINIDUMP_H 218