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 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 Optional
<ArrayRef
<uint8_t>> Stream
= getRawStream(StreamType::MemoryInfoList
);
60 return createError("No such stream");
62 getDataSliceAs
<minidump::MemoryInfoListHeader
>(*Stream
, 0, 1);
64 return ExpectedHeader
.takeError();
65 const minidump::MemoryInfoListHeader
&H
= ExpectedHeader
.get()[0];
66 Expected
<ArrayRef
<uint8_t>> Data
=
67 getDataSlice(*Stream
, H
.SizeOfHeader
, H
.SizeOfEntry
* H
.NumberOfEntries
);
69 return Data
.takeError();
70 return make_range(MemoryInfoIterator(*Data
, H
.SizeOfEntry
),
71 MemoryInfoIterator({}, H
.SizeOfEntry
));
75 Expected
<ArrayRef
<T
>> MinidumpFile::getListStream(StreamType Type
) const {
76 Optional
<ArrayRef
<uint8_t>> Stream
= getRawStream(Type
);
78 return createError("No such stream");
79 auto ExpectedSize
= getDataSliceAs
<support::ulittle32_t
>(*Stream
, 0, 1);
81 return ExpectedSize
.takeError();
83 size_t ListSize
= ExpectedSize
.get()[0];
85 size_t ListOffset
= 4;
86 // Some producers insert additional padding bytes to align the list to an
87 // 8-byte boundary. Check for that by comparing the list size with the overall
89 if (ListOffset
+ sizeof(T
) * ListSize
< Stream
->size())
92 return getDataSliceAs
<T
>(*Stream
, ListOffset
, ListSize
);
94 template Expected
<ArrayRef
<Module
>>
95 MinidumpFile::getListStream(StreamType
) const;
96 template Expected
<ArrayRef
<Thread
>>
97 MinidumpFile::getListStream(StreamType
) const;
98 template Expected
<ArrayRef
<MemoryDescriptor
>>
99 MinidumpFile::getListStream(StreamType
) const;
101 Expected
<ArrayRef
<uint8_t>>
102 MinidumpFile::getDataSlice(ArrayRef
<uint8_t> Data
, size_t Offset
, size_t Size
) {
103 // Check for overflow.
104 if (Offset
+ Size
< Offset
|| Offset
+ Size
< Size
||
105 Offset
+ Size
> Data
.size())
106 return createEOFError();
107 return Data
.slice(Offset
, Size
);
110 Expected
<std::unique_ptr
<MinidumpFile
>>
111 MinidumpFile::create(MemoryBufferRef Source
) {
112 ArrayRef
<uint8_t> Data
= arrayRefFromStringRef(Source
.getBuffer());
113 auto ExpectedHeader
= getDataSliceAs
<minidump::Header
>(Data
, 0, 1);
115 return ExpectedHeader
.takeError();
117 const minidump::Header
&Hdr
= (*ExpectedHeader
)[0];
118 if (Hdr
.Signature
!= Header::MagicSignature
)
119 return createError("Invalid signature");
120 if ((Hdr
.Version
& 0xffff) != Header::MagicVersion
)
121 return createError("Invalid version");
123 auto ExpectedStreams
= getDataSliceAs
<Directory
>(Data
, Hdr
.StreamDirectoryRVA
,
124 Hdr
.NumberOfStreams
);
125 if (!ExpectedStreams
)
126 return ExpectedStreams
.takeError();
128 DenseMap
<StreamType
, std::size_t> StreamMap
;
129 for (const auto &StreamDescriptor
: llvm::enumerate(*ExpectedStreams
)) {
130 StreamType Type
= StreamDescriptor
.value().Type
;
131 const LocationDescriptor
&Loc
= StreamDescriptor
.value().Location
;
133 Expected
<ArrayRef
<uint8_t>> Stream
=
134 getDataSlice(Data
, Loc
.RVA
, Loc
.DataSize
);
136 return Stream
.takeError();
138 if (Type
== StreamType::Unused
&& Loc
.DataSize
== 0) {
139 // Ignore dummy streams. This is technically ill-formed, but a number of
140 // existing minidumps seem to contain such streams.
144 if (Type
== DenseMapInfo
<StreamType
>::getEmptyKey() ||
145 Type
== DenseMapInfo
<StreamType
>::getTombstoneKey())
146 return createError("Cannot handle one of the minidump streams");
148 // Update the directory map, checking for duplicate stream types.
149 if (!StreamMap
.try_emplace(Type
, StreamDescriptor
.index()).second
)
150 return createError("Duplicate stream type");
153 return std::unique_ptr
<MinidumpFile
>(
154 new MinidumpFile(Source
, Hdr
, *ExpectedStreams
, std::move(StreamMap
)));