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");
57 Expected
<ArrayRef
<T
>> MinidumpFile::getListStream(StreamType Stream
) const {
58 auto OptionalStream
= getRawStream(Stream
);
60 return createError("No such stream");
62 getDataSliceAs
<support::ulittle32_t
>(*OptionalStream
, 0, 1);
64 return ExpectedSize
.takeError();
66 size_t ListSize
= ExpectedSize
.get()[0];
68 size_t ListOffset
= 4;
69 // Some producers insert additional padding bytes to align the list to an
70 // 8-byte boundary. Check for that by comparing the list size with the overall
72 if (ListOffset
+ sizeof(T
) * ListSize
< OptionalStream
->size())
75 return getDataSliceAs
<T
>(*OptionalStream
, ListOffset
, ListSize
);
77 template Expected
<ArrayRef
<Module
>>
78 MinidumpFile::getListStream(StreamType
) const;
79 template Expected
<ArrayRef
<Thread
>>
80 MinidumpFile::getListStream(StreamType
) const;
81 template Expected
<ArrayRef
<MemoryDescriptor
>>
82 MinidumpFile::getListStream(StreamType
) const;
84 Expected
<ArrayRef
<uint8_t>>
85 MinidumpFile::getDataSlice(ArrayRef
<uint8_t> Data
, size_t Offset
, size_t Size
) {
86 // Check for overflow.
87 if (Offset
+ Size
< Offset
|| Offset
+ Size
< Size
||
88 Offset
+ Size
> Data
.size())
89 return createEOFError();
90 return Data
.slice(Offset
, Size
);
93 Expected
<std::unique_ptr
<MinidumpFile
>>
94 MinidumpFile::create(MemoryBufferRef Source
) {
95 ArrayRef
<uint8_t> Data
= arrayRefFromStringRef(Source
.getBuffer());
96 auto ExpectedHeader
= getDataSliceAs
<minidump::Header
>(Data
, 0, 1);
98 return ExpectedHeader
.takeError();
100 const minidump::Header
&Hdr
= (*ExpectedHeader
)[0];
101 if (Hdr
.Signature
!= Header::MagicSignature
)
102 return createError("Invalid signature");
103 if ((Hdr
.Version
& 0xffff) != Header::MagicVersion
)
104 return createError("Invalid version");
106 auto ExpectedStreams
= getDataSliceAs
<Directory
>(Data
, Hdr
.StreamDirectoryRVA
,
107 Hdr
.NumberOfStreams
);
108 if (!ExpectedStreams
)
109 return ExpectedStreams
.takeError();
111 DenseMap
<StreamType
, std::size_t> StreamMap
;
112 for (const auto &Stream
: llvm::enumerate(*ExpectedStreams
)) {
113 StreamType Type
= Stream
.value().Type
;
114 const LocationDescriptor
&Loc
= Stream
.value().Location
;
116 auto ExpectedStream
= getDataSlice(Data
, Loc
.RVA
, Loc
.DataSize
);
118 return ExpectedStream
.takeError();
120 if (Type
== StreamType::Unused
&& Loc
.DataSize
== 0) {
121 // Ignore dummy streams. This is technically ill-formed, but a number of
122 // existing minidumps seem to contain such streams.
126 if (Type
== DenseMapInfo
<StreamType
>::getEmptyKey() ||
127 Type
== DenseMapInfo
<StreamType
>::getTombstoneKey())
128 return createError("Cannot handle one of the minidump streams");
130 // Update the directory map, checking for duplicate stream types.
131 if (!StreamMap
.try_emplace(Type
, Stream
.index()).second
)
132 return createError("Duplicate stream type");
135 return std::unique_ptr
<MinidumpFile
>(
136 new MinidumpFile(Source
, Hdr
, *ExpectedStreams
, std::move(StreamMap
)));