1 | //===- Minidump.cpp - Minidump object file implementation -----------------===// |
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 | #include "llvm/Object/Minidump.h" |
10 | #include "llvm/Object/Error.h" |
11 | #include "llvm/Support/ConvertUTF.h" |
12 | |
13 | using namespace llvm; |
14 | using namespace llvm::object; |
15 | using namespace llvm::minidump; |
16 | |
17 | std::optional<ArrayRef<uint8_t>> |
18 | MinidumpFile::getRawStream(minidump::StreamType Type) const { |
19 | auto It = StreamMap.find(Val: Type); |
20 | if (It != StreamMap.end()) |
21 | return getRawStream(Stream: Streams[It->second]); |
22 | return std::nullopt; |
23 | } |
24 | |
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 |
28 | // UTF16. |
29 | auto ExpectedSize = |
30 | getDataSliceAs<support::ulittle32_t>(Data: getData(), Offset, Count: 1); |
31 | if (!ExpectedSize) |
32 | return ExpectedSize.takeError(); |
33 | size_t Size = (*ExpectedSize)[0]; |
34 | if (Size % 2 != 0) |
35 | return createError(Str: "String size not even" ); |
36 | Size /= 2; |
37 | if (Size == 0) |
38 | return "" ; |
39 | |
40 | Offset += sizeof(support::ulittle32_t); |
41 | auto ExpectedData = |
42 | getDataSliceAs<support::ulittle16_t>(Data: getData(), Offset, Count: Size); |
43 | if (!ExpectedData) |
44 | return ExpectedData.takeError(); |
45 | |
46 | SmallVector<UTF16, 32> WStr(Size); |
47 | copy(Range&: *ExpectedData, Out: WStr.begin()); |
48 | |
49 | std::string Result; |
50 | if (!convertUTF16ToUTF8String(Src: WStr, Out&: Result)) |
51 | return createError(Str: "String decoding failed" ); |
52 | |
53 | return Result; |
54 | } |
55 | |
56 | Expected<iterator_range<MinidumpFile::MemoryInfoIterator>> |
57 | MinidumpFile::getMemoryInfoList() const { |
58 | std::optional<ArrayRef<uint8_t>> Stream = |
59 | getRawStream(Type: StreamType::MemoryInfoList); |
60 | if (!Stream) |
61 | return createError(Str: "No such stream" ); |
62 | auto = |
63 | getDataSliceAs<minidump::MemoryInfoListHeader>(Data: *Stream, Offset: 0, Count: 1); |
64 | if (!ExpectedHeader) |
65 | return ExpectedHeader.takeError(); |
66 | const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0]; |
67 | Expected<ArrayRef<uint8_t>> Data = |
68 | getDataSlice(Data: *Stream, Offset: H.SizeOfHeader, Size: H.SizeOfEntry * H.NumberOfEntries); |
69 | if (!Data) |
70 | return Data.takeError(); |
71 | return make_range(x: MemoryInfoIterator(*Data, H.SizeOfEntry), |
72 | y: MemoryInfoIterator({}, H.SizeOfEntry)); |
73 | } |
74 | |
75 | template <typename T> |
76 | Expected<ArrayRef<T>> MinidumpFile::getListStream(StreamType Type) const { |
77 | std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type); |
78 | if (!Stream) |
79 | return createError(Str: "No such stream" ); |
80 | auto ExpectedSize = getDataSliceAs<support::ulittle32_t>(Data: *Stream, Offset: 0, Count: 1); |
81 | if (!ExpectedSize) |
82 | return ExpectedSize.takeError(); |
83 | |
84 | size_t ListSize = ExpectedSize.get()[0]; |
85 | |
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 |
89 | // stream size. |
90 | if (ListOffset + sizeof(T) * ListSize < Stream->size()) |
91 | ListOffset = 8; |
92 | |
93 | return getDataSliceAs<T>(*Stream, ListOffset, ListSize); |
94 | } |
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; |
101 | |
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(N: Offset, M: Size); |
109 | } |
110 | |
111 | Expected<std::unique_ptr<MinidumpFile>> |
112 | MinidumpFile::create(MemoryBufferRef Source) { |
113 | ArrayRef<uint8_t> Data = arrayRefFromStringRef(Input: Source.getBuffer()); |
114 | auto = getDataSliceAs<minidump::Header>(Data, Offset: 0, Count: 1); |
115 | if (!ExpectedHeader) |
116 | return ExpectedHeader.takeError(); |
117 | |
118 | const minidump::Header &Hdr = (*ExpectedHeader)[0]; |
119 | if (Hdr.Signature != Header::MagicSignature) |
120 | return createError(Str: "Invalid signature" ); |
121 | if ((Hdr.Version & 0xffff) != Header::MagicVersion) |
122 | return createError(Str: "Invalid version" ); |
123 | |
124 | auto ExpectedStreams = getDataSliceAs<Directory>(Data, Offset: Hdr.StreamDirectoryRVA, |
125 | Count: Hdr.NumberOfStreams); |
126 | if (!ExpectedStreams) |
127 | return ExpectedStreams.takeError(); |
128 | |
129 | DenseMap<StreamType, std::size_t> StreamMap; |
130 | for (const auto &StreamDescriptor : llvm::enumerate(First&: *ExpectedStreams)) { |
131 | StreamType Type = StreamDescriptor.value().Type; |
132 | const LocationDescriptor &Loc = StreamDescriptor.value().Location; |
133 | |
134 | Expected<ArrayRef<uint8_t>> Stream = |
135 | getDataSlice(Data, Offset: Loc.RVA, Size: Loc.DataSize); |
136 | if (!Stream) |
137 | return Stream.takeError(); |
138 | |
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. |
142 | continue; |
143 | } |
144 | |
145 | if (Type == DenseMapInfo<StreamType>::getEmptyKey() || |
146 | Type == DenseMapInfo<StreamType>::getTombstoneKey()) |
147 | return createError(Str: "Cannot handle one of the minidump streams" ); |
148 | |
149 | // Update the directory map, checking for duplicate stream types. |
150 | if (!StreamMap.try_emplace(Key: Type, Args: StreamDescriptor.index()).second) |
151 | return createError(Str: "Duplicate stream type" ); |
152 | } |
153 | |
154 | return std::unique_ptr<MinidumpFile>( |
155 | new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap))); |
156 | } |
157 | |