1 //===- yaml2minidump.cpp - Convert a YAML file to a minidump file ---------===//
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/ObjectYAML/MinidumpYAML.h"
10 #include "llvm/ObjectYAML/yaml2obj.h"
11 #include "llvm/Support/ConvertUTF.h"
12 #include "llvm/Support/raw_ostream.h"
15 using namespace llvm::minidump
;
16 using namespace llvm::MinidumpYAML
;
19 /// A helper class to manage the placement of various structures into the final
20 /// minidump binary. Space for objects can be allocated via various allocate***
21 /// methods, while the final minidump file is written by calling the writeTo
22 /// method. The plain versions of allocation functions take a reference to the
23 /// data which is to be written (and hence the data must be available until
24 /// writeTo is called), while the "New" versions allocate the data in an
25 /// allocator-managed buffer, which is available until the allocator object is
26 /// destroyed. For both kinds of functions, it is possible to modify the
27 /// data for which the space has been "allocated" until the final writeTo call.
28 /// This is useful for "linking" the allocated structures via their offsets.
31 size_t tell() const { return NextOffset
; }
33 size_t allocateCallback(size_t Size
,
34 std::function
<void(raw_ostream
&)> Callback
) {
35 size_t Offset
= NextOffset
;
37 Callbacks
.push_back(std::move(Callback
));
41 size_t allocateBytes(ArrayRef
<uint8_t> Data
) {
42 return allocateCallback(
43 Data
.size(), [Data
](raw_ostream
&OS
) { OS
<< toStringRef(Data
); });
46 size_t allocateBytes(yaml::BinaryRef Data
) {
47 return allocateCallback(Data
.binary_size(), [Data
](raw_ostream
&OS
) {
48 Data
.writeAsBinary(OS
);
52 template <typename T
> size_t allocateArray(ArrayRef
<T
> Data
) {
53 return allocateBytes({reinterpret_cast<const uint8_t *>(Data
.data()),
54 sizeof(T
) * Data
.size()});
57 template <typename T
, typename RangeType
>
58 std::pair
<size_t, MutableArrayRef
<T
>>
59 allocateNewArray(const iterator_range
<RangeType
> &Range
);
61 template <typename T
> size_t allocateObject(const T
&Data
) {
62 return allocateArray(makeArrayRef(Data
));
65 template <typename T
, typename
... Types
>
66 std::pair
<size_t, T
*> allocateNewObject(Types
&&... Args
) {
67 T
*Object
= new (Temporaries
.Allocate
<T
>()) T(std::forward
<Types
>(Args
)...);
68 return {allocateObject(*Object
), Object
};
71 size_t allocateString(StringRef Str
);
73 void writeTo(raw_ostream
&OS
) const;
76 size_t NextOffset
= 0;
78 BumpPtrAllocator Temporaries
;
79 std::vector
<std::function
<void(raw_ostream
&)>> Callbacks
;
83 template <typename T
, typename RangeType
>
84 std::pair
<size_t, MutableArrayRef
<T
>>
85 BlobAllocator::allocateNewArray(const iterator_range
<RangeType
> &Range
) {
86 size_t Num
= std::distance(Range
.begin(), Range
.end());
87 MutableArrayRef
<T
> Array(Temporaries
.Allocate
<T
>(Num
), Num
);
88 std::uninitialized_copy(Range
.begin(), Range
.end(), Array
.begin());
89 return {allocateArray(Array
), Array
};
92 size_t BlobAllocator::allocateString(StringRef Str
) {
93 SmallVector
<UTF16
, 32> WStr
;
94 bool OK
= convertUTF8ToUTF16String(Str
, WStr
);
95 assert(OK
&& "Invalid UTF8 in Str?");
98 // The utf16 string is null-terminated, but the terminator is not counted in
102 allocateNewObject
<support::ulittle32_t
>(2 * (WStr
.size() - 1)).first
;
103 allocateNewArray
<support::ulittle16_t
>(make_range(WStr
.begin(), WStr
.end()));
107 void BlobAllocator::writeTo(raw_ostream
&OS
) const {
108 size_t BeginOffset
= OS
.tell();
109 for (const auto &Callback
: Callbacks
)
111 assert(OS
.tell() == BeginOffset
+ NextOffset
&&
112 "Callbacks wrote an unexpected number of bytes.");
116 static LocationDescriptor
layout(BlobAllocator
&File
, yaml::BinaryRef Data
) {
117 return {support::ulittle32_t(Data
.binary_size()),
118 support::ulittle32_t(File
.allocateBytes(Data
))};
121 static void layout(BlobAllocator
&File
, MemoryListStream::entry_type
&Range
) {
122 Range
.Entry
.Memory
= layout(File
, Range
.Content
);
125 static void layout(BlobAllocator
&File
, ModuleListStream::entry_type
&M
) {
126 M
.Entry
.ModuleNameRVA
= File
.allocateString(M
.Name
);
128 M
.Entry
.CvRecord
= layout(File
, M
.CvRecord
);
129 M
.Entry
.MiscRecord
= layout(File
, M
.MiscRecord
);
132 static void layout(BlobAllocator
&File
, ThreadListStream::entry_type
&T
) {
133 T
.Entry
.Stack
.Memory
= layout(File
, T
.Stack
);
134 T
.Entry
.Context
= layout(File
, T
.Context
);
137 template <typename EntryT
>
138 static size_t layout(BlobAllocator
&File
,
139 MinidumpYAML::detail::ListStream
<EntryT
> &S
) {
141 File
.allocateNewObject
<support::ulittle32_t
>(S
.Entries
.size());
142 for (auto &E
: S
.Entries
)
143 File
.allocateObject(E
.Entry
);
145 size_t DataEnd
= File
.tell();
147 // Lay out the auxiliary data, (which is not a part of the stream).
148 DataEnd
= File
.tell();
149 for (auto &E
: S
.Entries
)
155 static Directory
layout(BlobAllocator
&File
, Stream
&S
) {
157 Result
.Type
= S
.Type
;
158 Result
.Location
.RVA
= File
.tell();
159 Optional
<size_t> DataEnd
;
161 case Stream::StreamKind::MemoryList
:
162 DataEnd
= layout(File
, cast
<MemoryListStream
>(S
));
164 case Stream::StreamKind::ModuleList
:
165 DataEnd
= layout(File
, cast
<ModuleListStream
>(S
));
167 case Stream::StreamKind::RawContent
: {
168 RawContentStream
&Raw
= cast
<RawContentStream
>(S
);
169 File
.allocateCallback(Raw
.Size
, [&Raw
](raw_ostream
&OS
) {
170 Raw
.Content
.writeAsBinary(OS
);
171 assert(Raw
.Content
.binary_size() <= Raw
.Size
);
172 OS
<< std::string(Raw
.Size
- Raw
.Content
.binary_size(), '\0');
176 case Stream::StreamKind::SystemInfo
: {
177 SystemInfoStream
&SystemInfo
= cast
<SystemInfoStream
>(S
);
178 File
.allocateObject(SystemInfo
.Info
);
179 // The CSD string is not a part of the stream.
180 DataEnd
= File
.tell();
181 SystemInfo
.Info
.CSDVersionRVA
= File
.allocateString(SystemInfo
.CSDVersion
);
184 case Stream::StreamKind::TextContent
:
185 File
.allocateArray(arrayRefFromStringRef(cast
<TextContentStream
>(S
).Text
));
187 case Stream::StreamKind::ThreadList
:
188 DataEnd
= layout(File
, cast
<ThreadListStream
>(S
));
191 // If DataEnd is not set, we assume everything we generated is a part of the
193 Result
.Location
.DataSize
=
194 DataEnd
.getValueOr(File
.tell()) - Result
.Location
.RVA
;
201 int yaml2minidump(MinidumpYAML::Object
&Obj
, raw_ostream
&Out
) {
203 File
.allocateObject(Obj
.Header
);
205 std::vector
<Directory
> StreamDirectory(Obj
.Streams
.size());
206 Obj
.Header
.StreamDirectoryRVA
=
207 File
.allocateArray(makeArrayRef(StreamDirectory
));
208 Obj
.Header
.NumberOfStreams
= StreamDirectory
.size();
210 for (auto &Stream
: enumerate(Obj
.Streams
))
211 StreamDirectory
[Stream
.index()] = layout(File
, *Stream
.value());