1 //===- Minidump.cpp - Minidump object file implementation -----------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Object/Minidump.h"
10 #include "llvm/Object/Error.h"
11 #include "llvm/Support/ConvertUTF.h"
14 using namespace llvm::object
;
15 using namespace llvm::minidump
;
17 std::optional
<ArrayRef
<uint8_t>>
18 MinidumpFile::getRawStream(minidump::StreamType Type
) const {
19 auto It
= StreamMap
.find(Type
);
20 if (It
!= StreamMap
.end())
21 return getRawStream(Streams
[It
->second
]);
25 Expected
<std::string
> MinidumpFile::getString(size_t Offset
) const {
26 // Minidump strings consist of a 32-bit length field, which gives the size of
27 // the string in *bytes*. This is followed by the actual string encoded in
30 getDataSliceAs
<support::ulittle32_t
>(getData(), Offset
, 1);
32 return ExpectedSize
.takeError();
33 size_t Size
= (*ExpectedSize
)[0];
35 return createError("String size not even");
40 Offset
+= sizeof(support::ulittle32_t
);
42 getDataSliceAs
<support::ulittle16_t
>(getData(), Offset
, Size
);
44 return ExpectedData
.takeError();
46 SmallVector
<UTF16
, 32> WStr(Size
);
47 copy(*ExpectedData
, WStr
.begin());
50 if (!convertUTF16ToUTF8String(WStr
, Result
))
51 return createError("String decoding failed");
56 Expected
<iterator_range
<MinidumpFile::MemoryInfoIterator
>>
57 MinidumpFile::getMemoryInfoList() const {
58 std::optional
<ArrayRef
<uint8_t>> Stream
=
59 getRawStream(StreamType::MemoryInfoList
);
61 return createError("No such stream");
63 getDataSliceAs
<minidump::MemoryInfoListHeader
>(*Stream
, 0, 1);
65 return ExpectedHeader
.takeError();
66 const minidump::MemoryInfoListHeader
&H
= ExpectedHeader
.get()[0];
67 Expected
<ArrayRef
<uint8_t>> Data
=
68 getDataSlice(*Stream
, H
.SizeOfHeader
, H
.SizeOfEntry
* H
.NumberOfEntries
);
70 return Data
.takeError();
71 return make_range(MemoryInfoIterator(*Data
, H
.SizeOfEntry
),
72 MemoryInfoIterator({}, H
.SizeOfEntry
));
76 Expected
<ArrayRef
<T
>> MinidumpFile::getListStream(StreamType Type
) const {
77 std::optional
<ArrayRef
<uint8_t>> Stream
= getRawStream(Type
);
79 return createError("No such stream");
80 auto ExpectedSize
= getDataSliceAs
<support::ulittle32_t
>(*Stream
, 0, 1);
82 return ExpectedSize
.takeError();
84 size_t ListSize
= ExpectedSize
.get()[0];
86 size_t ListOffset
= 4;
87 // Some producers insert additional padding bytes to align the list to an
88 // 8-byte boundary. Check for that by comparing the list size with the overall
90 if (ListOffset
+ sizeof(T
) * ListSize
< Stream
->size())
93 return getDataSliceAs
<T
>(*Stream
, ListOffset
, ListSize
);
95 template Expected
<ArrayRef
<Module
>>
96 MinidumpFile::getListStream(StreamType
) const;
97 template Expected
<ArrayRef
<Thread
>>
98 MinidumpFile::getListStream(StreamType
) const;
99 template Expected
<ArrayRef
<MemoryDescriptor
>>
100 MinidumpFile::getListStream(StreamType
) const;
102 Expected
<ArrayRef
<uint8_t>>
103 MinidumpFile::getDataSlice(ArrayRef
<uint8_t> Data
, size_t Offset
, size_t Size
) {
104 // Check for overflow.
105 if (Offset
+ Size
< Offset
|| Offset
+ Size
< Size
||
106 Offset
+ Size
> Data
.size())
107 return createEOFError();
108 return Data
.slice(Offset
, Size
);
111 Expected
<std::unique_ptr
<MinidumpFile
>>
112 MinidumpFile::create(MemoryBufferRef Source
) {
113 ArrayRef
<uint8_t> Data
= arrayRefFromStringRef(Source
.getBuffer());
114 auto ExpectedHeader
= getDataSliceAs
<minidump::Header
>(Data
, 0, 1);
116 return ExpectedHeader
.takeError();
118 const minidump::Header
&Hdr
= (*ExpectedHeader
)[0];
119 if (Hdr
.Signature
!= Header::MagicSignature
)
120 return createError("Invalid signature");
121 if ((Hdr
.Version
& 0xffff) != Header::MagicVersion
)
122 return createError("Invalid version");
124 auto ExpectedStreams
= getDataSliceAs
<Directory
>(Data
, Hdr
.StreamDirectoryRVA
,
125 Hdr
.NumberOfStreams
);
126 if (!ExpectedStreams
)
127 return ExpectedStreams
.takeError();
129 DenseMap
<StreamType
, std::size_t> StreamMap
;
130 for (const auto &StreamDescriptor
: llvm::enumerate(*ExpectedStreams
)) {
131 StreamType Type
= StreamDescriptor
.value().Type
;
132 const LocationDescriptor
&Loc
= StreamDescriptor
.value().Location
;
134 Expected
<ArrayRef
<uint8_t>> Stream
=
135 getDataSlice(Data
, Loc
.RVA
, Loc
.DataSize
);
137 return Stream
.takeError();
139 if (Type
== StreamType::Unused
&& Loc
.DataSize
== 0) {
140 // Ignore dummy streams. This is technically ill-formed, but a number of
141 // existing minidumps seem to contain such streams.
145 if (Type
== DenseMapInfo
<StreamType
>::getEmptyKey() ||
146 Type
== DenseMapInfo
<StreamType
>::getTombstoneKey())
147 return createError("Cannot handle one of the minidump streams");
149 // Update the directory map, checking for duplicate stream types.
150 if (!StreamMap
.try_emplace(Type
, StreamDescriptor
.index()).second
)
151 return createError("Duplicate stream type");
154 return std::unique_ptr
<MinidumpFile
>(
155 new MinidumpFile(Source
, Hdr
, *ExpectedStreams
, std::move(StreamMap
)));