• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/fallible_iterator.h"
15 #include "llvm/ADT/iterator.h"
16 #include "llvm/BinaryFormat/Minidump.h"
17 #include "llvm/Object/Binary.h"
18 #include "llvm/Support/Error.h"
19 
20 namespace llvm {
21 namespace object {
22 
23 /// A class providing access to the contents of a minidump file.
24 class MinidumpFile : public Binary {
25 public:
26   /// Construct a new MinidumpFile object from the given memory buffer. Returns
27   /// an error if this file cannot be identified as a minidump file, or if its
28   /// contents are badly corrupted (i.e. we cannot read the stream directory).
29   static Expected<std::unique_ptr<MinidumpFile>> create(MemoryBufferRef Source);
30 
classof(const Binary * B)31   static bool classof(const Binary *B) { return B->isMinidump(); }
32 
33   /// Returns the contents of the minidump header.
header()34   const minidump::Header &header() const { return Header; }
35 
36   /// Returns the list of streams (stream directory entries) in this file.
streams()37   ArrayRef<minidump::Directory> streams() const { return Streams; }
38 
39   /// Returns the raw contents of the stream given by the directory entry.
getRawStream(const minidump::Directory & Stream)40   ArrayRef<uint8_t> getRawStream(const minidump::Directory &Stream) const {
41     return getData().slice(Stream.Location.RVA, Stream.Location.DataSize);
42   }
43 
44   /// Returns the raw contents of the stream of the given type, or std::nullopt
45   /// if the file does not contain a stream of this type.
46   std::optional<ArrayRef<uint8_t>>
47   getRawStream(minidump::StreamType Type) const;
48 
49   /// Returns the raw contents of an object given by the LocationDescriptor. An
50   /// error is returned if the descriptor points outside of the minidump file.
51   Expected<ArrayRef<uint8_t>>
getRawData(minidump::LocationDescriptor Desc)52   getRawData(minidump::LocationDescriptor Desc) const {
53     return getDataSlice(getData(), Desc.RVA, Desc.DataSize);
54   }
55 
56   /// Returns the minidump string at the given offset. An error is returned if
57   /// we fail to parse the string, or the string is invalid UTF16.
58   Expected<std::string> getString(size_t Offset) const;
59 
60   /// Returns the contents of the SystemInfo stream, cast to the appropriate
61   /// type. An error is returned if the file does not contain this stream, or
62   /// the stream is smaller than the size of the SystemInfo structure. The
63   /// internal consistency of the stream is not checked in any way.
getSystemInfo()64   Expected<const minidump::SystemInfo &> getSystemInfo() const {
65     return getStream<minidump::SystemInfo>(minidump::StreamType::SystemInfo);
66   }
67 
68   /// Returns the module list embedded in the ModuleList stream. An error is
69   /// returned if the file does not contain this stream, or if the stream is
70   /// not large enough to contain the number of modules declared in the stream
71   /// header. The consistency of the Module entries themselves is not checked in
72   /// any way.
getModuleList()73   Expected<ArrayRef<minidump::Module>> getModuleList() const {
74     return getListStream<minidump::Module>(minidump::StreamType::ModuleList);
75   }
76 
77   /// Returns the thread list embedded in the ThreadList stream. An error is
78   /// returned if the file does not contain this stream, or if the stream is
79   /// not large enough to contain the number of threads declared in the stream
80   /// header. The consistency of the Thread entries themselves is not checked in
81   /// any way.
getThreadList()82   Expected<ArrayRef<minidump::Thread>> getThreadList() const {
83     return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
84   }
85 
86   /// Returns the contents of the Exception stream.  An error is returned if the
87   /// file does not contain this stream, or the stream is smaller than the size
88   /// of the ExceptionStream structure.  The internal consistency of the stream
89   /// is not checked in any way.
getExceptionStream()90   Expected<const minidump::ExceptionStream &> getExceptionStream() const {
91     return getStream<minidump::ExceptionStream>(
92         minidump::StreamType::Exception);
93   }
94 
95   /// Returns the list of descriptors embedded in the MemoryList stream. The
96   /// descriptors provide the content of interesting regions of memory at the
97   /// time the minidump was taken. An error is returned if the file does not
98   /// contain this stream, or if the stream is not large enough to contain the
99   /// number of memory descriptors declared in the stream header. The
100   /// consistency of the MemoryDescriptor entries themselves is not checked in
101   /// any way.
getMemoryList()102   Expected<ArrayRef<minidump::MemoryDescriptor>> getMemoryList() const {
103     return getListStream<minidump::MemoryDescriptor>(
104         minidump::StreamType::MemoryList);
105   }
106 
107   /// Returns the header to the memory 64 list stream. An error is returned if
108   /// the file does not contain this stream.
getMemoryList64Header()109   Expected<minidump::Memory64ListHeader> getMemoryList64Header() const {
110     return getStream<minidump::Memory64ListHeader>(
111         minidump::StreamType::Memory64List);
112   }
113 
114   class MemoryInfoIterator
115       : public iterator_facade_base<MemoryInfoIterator,
116                                     std::forward_iterator_tag,
117                                     minidump::MemoryInfo> {
118   public:
MemoryInfoIterator(ArrayRef<uint8_t> Storage,size_t Stride)119     MemoryInfoIterator(ArrayRef<uint8_t> Storage, size_t Stride)
120         : Storage(Storage), Stride(Stride) {
121       assert(Storage.size() % Stride == 0);
122     }
123 
124     bool operator==(const MemoryInfoIterator &R) const {
125       return Storage.size() == R.Storage.size();
126     }
127 
128     const minidump::MemoryInfo &operator*() const {
129       assert(Storage.size() >= sizeof(minidump::MemoryInfo));
130       return *reinterpret_cast<const minidump::MemoryInfo *>(Storage.data());
131     }
132 
133     MemoryInfoIterator &operator++() {
134       Storage = Storage.drop_front(Stride);
135       return *this;
136     }
137 
138   private:
139     ArrayRef<uint8_t> Storage;
140     size_t Stride;
141   };
142 
143   /// Class the provides an iterator over the memory64 memory ranges. Only the
144   /// the first descriptor is validated as readable beforehand.
145   class Memory64Iterator {
146   public:
147     static Memory64Iterator
begin(ArrayRef<uint8_t> Storage,ArrayRef<minidump::MemoryDescriptor_64> Descriptors)148     begin(ArrayRef<uint8_t> Storage,
149           ArrayRef<minidump::MemoryDescriptor_64> Descriptors) {
150       return Memory64Iterator(Storage, Descriptors);
151     }
152 
end()153     static Memory64Iterator end() { return Memory64Iterator(); }
154 
155     bool operator==(const Memory64Iterator &R) const {
156       return IsEnd == R.IsEnd;
157     }
158 
159     bool operator!=(const Memory64Iterator &R) const { return !(*this == R); }
160 
161     const std::pair<minidump::MemoryDescriptor_64, ArrayRef<uint8_t>> &
162     operator*() {
163       return Current;
164     }
165 
166     const std::pair<minidump::MemoryDescriptor_64, ArrayRef<uint8_t>> *
167     operator->() {
168       return &Current;
169     }
170 
inc()171     Error inc() {
172       if (Descriptors.empty()) {
173         IsEnd = true;
174         return Error::success();
175       }
176 
177       // Drop front gives us an array ref, so we need to call .front() as well.
178       const minidump::MemoryDescriptor_64 &Descriptor = Descriptors.front();
179       if (Descriptor.DataSize > Storage.size()) {
180         IsEnd = true;
181         return make_error<GenericBinaryError>(
182             "Memory64 Descriptor exceeds end of file.",
183             object_error::unexpected_eof);
184       }
185 
186       ArrayRef<uint8_t> Content = Storage.take_front(Descriptor.DataSize);
187       Current = std::make_pair(Descriptor, Content);
188 
189       Storage = Storage.drop_front(Descriptor.DataSize);
190       Descriptors = Descriptors.drop_front();
191 
192       return Error::success();
193     }
194 
195   private:
196     // This constructor expects that the first descriptor is readable.
Memory64Iterator(ArrayRef<uint8_t> Storage,ArrayRef<minidump::MemoryDescriptor_64> Descriptors)197     Memory64Iterator(ArrayRef<uint8_t> Storage,
198                      ArrayRef<minidump::MemoryDescriptor_64> Descriptors)
199         : Storage(Storage), Descriptors(Descriptors), IsEnd(false) {
200       assert(!Descriptors.empty() &&
201              Storage.size() >= Descriptors.front().DataSize);
202       minidump::MemoryDescriptor_64 Descriptor = Descriptors.front();
203       ArrayRef<uint8_t> Content = Storage.take_front(Descriptor.DataSize);
204       Current = std::make_pair(Descriptor, Content);
205       this->Descriptors = Descriptors.drop_front();
206       this->Storage = Storage.drop_front(Descriptor.DataSize);
207     }
208 
Memory64Iterator()209     Memory64Iterator()
210         : Storage(ArrayRef<uint8_t>()),
211           Descriptors(ArrayRef<minidump::MemoryDescriptor_64>()), IsEnd(true) {}
212 
213     std::pair<minidump::MemoryDescriptor_64, ArrayRef<uint8_t>> Current;
214     ArrayRef<uint8_t> Storage;
215     ArrayRef<minidump::MemoryDescriptor_64> Descriptors;
216     bool IsEnd;
217   };
218 
219   using FallibleMemory64Iterator = llvm::fallible_iterator<Memory64Iterator>;
220 
221   /// Returns an iterator that pairs each descriptor with it's respective
222   /// content from the Memory64List stream. An error is returned if the file
223   /// does not contain a Memory64List stream, or if the descriptor data is
224   /// unreadable.
225   iterator_range<FallibleMemory64Iterator> getMemory64List(Error &Err) const;
226 
227   /// Returns the list of descriptors embedded in the MemoryInfoList stream. The
228   /// descriptors provide properties (e.g. permissions) of interesting regions
229   /// of memory at the time the minidump was taken. An error is returned if the
230   /// file does not contain this stream, or if the stream is not large enough to
231   /// contain the number of memory descriptors declared in the stream header.
232   /// The consistency of the MemoryInfoList entries themselves is not checked
233   /// in any way.
234   Expected<iterator_range<MemoryInfoIterator>> getMemoryInfoList() const;
235 
236 private:
createError(StringRef Str)237   static Error createError(StringRef Str) {
238     return make_error<GenericBinaryError>(Str, object_error::parse_failed);
239   }
240 
createEOFError()241   static Error createEOFError() {
242     return make_error<GenericBinaryError>("Unexpected EOF",
243                                           object_error::unexpected_eof);
244   }
245 
246   /// Return a slice of the given data array, with bounds checking.
247   static Expected<ArrayRef<uint8_t>>
248   getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size);
249 
250   /// Return the slice of the given data array as an array of objects of the
251   /// given type. The function checks that the input array is large enough to
252   /// contain the correct number of objects of the given type.
253   template <typename T>
254   static Expected<ArrayRef<T>> getDataSliceAs(ArrayRef<uint8_t> Data,
255                                               uint64_t Offset, uint64_t Count);
256 
MinidumpFile(MemoryBufferRef Source,const minidump::Header & Header,ArrayRef<minidump::Directory> Streams,DenseMap<minidump::StreamType,std::size_t> StreamMap)257   MinidumpFile(MemoryBufferRef Source, const minidump::Header &Header,
258                ArrayRef<minidump::Directory> Streams,
259                DenseMap<minidump::StreamType, std::size_t> StreamMap)
260       : Binary(ID_Minidump, Source), Header(Header), Streams(Streams),
261         StreamMap(std::move(StreamMap)) {}
262 
getData()263   ArrayRef<uint8_t> getData() const {
264     return arrayRefFromStringRef(Data.getBuffer());
265   }
266 
267   /// Return the stream of the given type, cast to the appropriate type. Checks
268   /// that the stream is large enough to hold an object of this type.
269   template <typename T>
270   Expected<const T &> getStream(minidump::StreamType Stream) const;
271 
272   /// Return the contents of a stream which contains a list of fixed-size items,
273   /// prefixed by the list size.
274   template <typename T>
275   Expected<ArrayRef<T>> getListStream(minidump::StreamType Stream) const;
276 
277   const minidump::Header &Header;
278   ArrayRef<minidump::Directory> Streams;
279   DenseMap<minidump::StreamType, std::size_t> StreamMap;
280 };
281 
282 template <typename T>
getStream(minidump::StreamType Type)283 Expected<const T &> MinidumpFile::getStream(minidump::StreamType Type) const {
284   if (std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type)) {
285     if (Stream->size() >= sizeof(T))
286       return *reinterpret_cast<const T *>(Stream->data());
287     return createEOFError();
288   }
289   return createError("No such stream");
290 }
291 
292 template <typename T>
getDataSliceAs(ArrayRef<uint8_t> Data,uint64_t Offset,uint64_t Count)293 Expected<ArrayRef<T>> MinidumpFile::getDataSliceAs(ArrayRef<uint8_t> Data,
294                                                    uint64_t Offset,
295                                                    uint64_t Count) {
296   // Check for overflow.
297   if (Count > std::numeric_limits<uint64_t>::max() / sizeof(T))
298     return createEOFError();
299   Expected<ArrayRef<uint8_t>> Slice =
300       getDataSlice(Data, Offset, sizeof(T) * Count);
301   if (!Slice)
302     return Slice.takeError();
303 
304   return ArrayRef<T>(reinterpret_cast<const T *>(Slice->data()), Count);
305 }
306 
307 } // end namespace object
308 } // end namespace llvm
309 
310 #endif // LLVM_OBJECT_MINIDUMP_H
311