xref: /netbsd-src/external/apache2/llvm/dist/llvm/lib/Object/Minidump.cpp (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1*7330f729Sjoerg //===- Minidump.cpp - Minidump object file implementation -----------------===//
2*7330f729Sjoerg //
3*7330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*7330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7330f729Sjoerg //
7*7330f729Sjoerg //===----------------------------------------------------------------------===//
8*7330f729Sjoerg 
9*7330f729Sjoerg #include "llvm/Object/Minidump.h"
10*7330f729Sjoerg #include "llvm/Object/Error.h"
11*7330f729Sjoerg #include "llvm/Support/ConvertUTF.h"
12*7330f729Sjoerg 
13*7330f729Sjoerg using namespace llvm;
14*7330f729Sjoerg using namespace llvm::object;
15*7330f729Sjoerg using namespace llvm::minidump;
16*7330f729Sjoerg 
17*7330f729Sjoerg Optional<ArrayRef<uint8_t>>
getRawStream(minidump::StreamType Type) const18*7330f729Sjoerg MinidumpFile::getRawStream(minidump::StreamType Type) const {
19*7330f729Sjoerg   auto It = StreamMap.find(Type);
20*7330f729Sjoerg   if (It != StreamMap.end())
21*7330f729Sjoerg     return getRawStream(Streams[It->second]);
22*7330f729Sjoerg   return None;
23*7330f729Sjoerg }
24*7330f729Sjoerg 
getString(size_t Offset) const25*7330f729Sjoerg Expected<std::string> MinidumpFile::getString(size_t Offset) const {
26*7330f729Sjoerg   // Minidump strings consist of a 32-bit length field, which gives the size of
27*7330f729Sjoerg   // the string in *bytes*. This is followed by the actual string encoded in
28*7330f729Sjoerg   // UTF16.
29*7330f729Sjoerg   auto ExpectedSize =
30*7330f729Sjoerg       getDataSliceAs<support::ulittle32_t>(getData(), Offset, 1);
31*7330f729Sjoerg   if (!ExpectedSize)
32*7330f729Sjoerg     return ExpectedSize.takeError();
33*7330f729Sjoerg   size_t Size = (*ExpectedSize)[0];
34*7330f729Sjoerg   if (Size % 2 != 0)
35*7330f729Sjoerg     return createError("String size not even");
36*7330f729Sjoerg   Size /= 2;
37*7330f729Sjoerg   if (Size == 0)
38*7330f729Sjoerg     return "";
39*7330f729Sjoerg 
40*7330f729Sjoerg   Offset += sizeof(support::ulittle32_t);
41*7330f729Sjoerg   auto ExpectedData =
42*7330f729Sjoerg       getDataSliceAs<support::ulittle16_t>(getData(), Offset, Size);
43*7330f729Sjoerg   if (!ExpectedData)
44*7330f729Sjoerg     return ExpectedData.takeError();
45*7330f729Sjoerg 
46*7330f729Sjoerg   SmallVector<UTF16, 32> WStr(Size);
47*7330f729Sjoerg   copy(*ExpectedData, WStr.begin());
48*7330f729Sjoerg 
49*7330f729Sjoerg   std::string Result;
50*7330f729Sjoerg   if (!convertUTF16ToUTF8String(WStr, Result))
51*7330f729Sjoerg     return createError("String decoding failed");
52*7330f729Sjoerg 
53*7330f729Sjoerg   return Result;
54*7330f729Sjoerg }
55*7330f729Sjoerg 
56*7330f729Sjoerg Expected<iterator_range<MinidumpFile::MemoryInfoIterator>>
getMemoryInfoList() const57*7330f729Sjoerg MinidumpFile::getMemoryInfoList() const {
58*7330f729Sjoerg   Optional<ArrayRef<uint8_t>> Stream = getRawStream(StreamType::MemoryInfoList);
59*7330f729Sjoerg   if (!Stream)
60*7330f729Sjoerg     return createError("No such stream");
61*7330f729Sjoerg   auto ExpectedHeader =
62*7330f729Sjoerg       getDataSliceAs<minidump::MemoryInfoListHeader>(*Stream, 0, 1);
63*7330f729Sjoerg   if (!ExpectedHeader)
64*7330f729Sjoerg     return ExpectedHeader.takeError();
65*7330f729Sjoerg   const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0];
66*7330f729Sjoerg   Expected<ArrayRef<uint8_t>> Data =
67*7330f729Sjoerg       getDataSlice(*Stream, H.SizeOfHeader, H.SizeOfEntry * H.NumberOfEntries);
68*7330f729Sjoerg   if (!Data)
69*7330f729Sjoerg     return Data.takeError();
70*7330f729Sjoerg   return make_range(MemoryInfoIterator(*Data, H.SizeOfEntry),
71*7330f729Sjoerg                     MemoryInfoIterator({}, H.SizeOfEntry));
72*7330f729Sjoerg }
73*7330f729Sjoerg 
74*7330f729Sjoerg template <typename T>
getListStream(StreamType Type) const75*7330f729Sjoerg Expected<ArrayRef<T>> MinidumpFile::getListStream(StreamType Type) const {
76*7330f729Sjoerg   Optional<ArrayRef<uint8_t>> Stream = getRawStream(Type);
77*7330f729Sjoerg   if (!Stream)
78*7330f729Sjoerg     return createError("No such stream");
79*7330f729Sjoerg   auto ExpectedSize = getDataSliceAs<support::ulittle32_t>(*Stream, 0, 1);
80*7330f729Sjoerg   if (!ExpectedSize)
81*7330f729Sjoerg     return ExpectedSize.takeError();
82*7330f729Sjoerg 
83*7330f729Sjoerg   size_t ListSize = ExpectedSize.get()[0];
84*7330f729Sjoerg 
85*7330f729Sjoerg   size_t ListOffset = 4;
86*7330f729Sjoerg   // Some producers insert additional padding bytes to align the list to an
87*7330f729Sjoerg   // 8-byte boundary. Check for that by comparing the list size with the overall
88*7330f729Sjoerg   // stream size.
89*7330f729Sjoerg   if (ListOffset + sizeof(T) * ListSize < Stream->size())
90*7330f729Sjoerg     ListOffset = 8;
91*7330f729Sjoerg 
92*7330f729Sjoerg   return getDataSliceAs<T>(*Stream, ListOffset, ListSize);
93*7330f729Sjoerg }
94*7330f729Sjoerg template Expected<ArrayRef<Module>>
95*7330f729Sjoerg     MinidumpFile::getListStream(StreamType) const;
96*7330f729Sjoerg template Expected<ArrayRef<Thread>>
97*7330f729Sjoerg     MinidumpFile::getListStream(StreamType) const;
98*7330f729Sjoerg template Expected<ArrayRef<MemoryDescriptor>>
99*7330f729Sjoerg     MinidumpFile::getListStream(StreamType) const;
100*7330f729Sjoerg 
101*7330f729Sjoerg Expected<ArrayRef<uint8_t>>
getDataSlice(ArrayRef<uint8_t> Data,size_t Offset,size_t Size)102*7330f729Sjoerg MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) {
103*7330f729Sjoerg   // Check for overflow.
104*7330f729Sjoerg   if (Offset + Size < Offset || Offset + Size < Size ||
105*7330f729Sjoerg       Offset + Size > Data.size())
106*7330f729Sjoerg     return createEOFError();
107*7330f729Sjoerg   return Data.slice(Offset, Size);
108*7330f729Sjoerg }
109*7330f729Sjoerg 
110*7330f729Sjoerg Expected<std::unique_ptr<MinidumpFile>>
create(MemoryBufferRef Source)111*7330f729Sjoerg MinidumpFile::create(MemoryBufferRef Source) {
112*7330f729Sjoerg   ArrayRef<uint8_t> Data = arrayRefFromStringRef(Source.getBuffer());
113*7330f729Sjoerg   auto ExpectedHeader = getDataSliceAs<minidump::Header>(Data, 0, 1);
114*7330f729Sjoerg   if (!ExpectedHeader)
115*7330f729Sjoerg     return ExpectedHeader.takeError();
116*7330f729Sjoerg 
117*7330f729Sjoerg   const minidump::Header &Hdr = (*ExpectedHeader)[0];
118*7330f729Sjoerg   if (Hdr.Signature != Header::MagicSignature)
119*7330f729Sjoerg     return createError("Invalid signature");
120*7330f729Sjoerg   if ((Hdr.Version & 0xffff) != Header::MagicVersion)
121*7330f729Sjoerg     return createError("Invalid version");
122*7330f729Sjoerg 
123*7330f729Sjoerg   auto ExpectedStreams = getDataSliceAs<Directory>(Data, Hdr.StreamDirectoryRVA,
124*7330f729Sjoerg                                                    Hdr.NumberOfStreams);
125*7330f729Sjoerg   if (!ExpectedStreams)
126*7330f729Sjoerg     return ExpectedStreams.takeError();
127*7330f729Sjoerg 
128*7330f729Sjoerg   DenseMap<StreamType, std::size_t> StreamMap;
129*7330f729Sjoerg   for (const auto &StreamDescriptor : llvm::enumerate(*ExpectedStreams)) {
130*7330f729Sjoerg     StreamType Type = StreamDescriptor.value().Type;
131*7330f729Sjoerg     const LocationDescriptor &Loc = StreamDescriptor.value().Location;
132*7330f729Sjoerg 
133*7330f729Sjoerg     Expected<ArrayRef<uint8_t>> Stream =
134*7330f729Sjoerg         getDataSlice(Data, Loc.RVA, Loc.DataSize);
135*7330f729Sjoerg     if (!Stream)
136*7330f729Sjoerg       return Stream.takeError();
137*7330f729Sjoerg 
138*7330f729Sjoerg     if (Type == StreamType::Unused && Loc.DataSize == 0) {
139*7330f729Sjoerg       // Ignore dummy streams. This is technically ill-formed, but a number of
140*7330f729Sjoerg       // existing minidumps seem to contain such streams.
141*7330f729Sjoerg       continue;
142*7330f729Sjoerg     }
143*7330f729Sjoerg 
144*7330f729Sjoerg     if (Type == DenseMapInfo<StreamType>::getEmptyKey() ||
145*7330f729Sjoerg         Type == DenseMapInfo<StreamType>::getTombstoneKey())
146*7330f729Sjoerg       return createError("Cannot handle one of the minidump streams");
147*7330f729Sjoerg 
148*7330f729Sjoerg     // Update the directory map, checking for duplicate stream types.
149*7330f729Sjoerg     if (!StreamMap.try_emplace(Type, StreamDescriptor.index()).second)
150*7330f729Sjoerg       return createError("Duplicate stream type");
151*7330f729Sjoerg   }
152*7330f729Sjoerg 
153*7330f729Sjoerg   return std::unique_ptr<MinidumpFile>(
154*7330f729Sjoerg       new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap)));
155*7330f729Sjoerg }
156