Skip to content

Commit b1edac0

Browse files
authored
[Obj2Yaml] Add support for minidump generation with 64b memory ranges. (llvm#101272)
This PR adds support for `obj2yaml` and `yaml2obj` to generate minidumps that have a Memory64List stream. This is a prerequisite to llvm#101086. Worth noting - ~~const dropped on minidumps so we could cache a MemoryDescriptor_64 to it's actual offset, preventing the need to loop multiple times~~ - doesn't reuse the existing `ListStream` code in some places, because the Memory64List has a different width size field (unsigned 64), and a larger header than all the other streams. I determined refactoring the existing code to support Mem64 would be worse than supporting the special case.
1 parent ecbbe5b commit b1edac0

File tree

8 files changed

+333
-10
lines changed

8 files changed

+333
-10
lines changed

llvm/include/llvm/BinaryFormat/Minidump.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ struct MemoryDescriptor_64 {
7474
support::ulittle64_t StartOfMemoryRange;
7575
support::ulittle64_t DataSize;
7676
};
77+
static_assert(sizeof(MemoryDescriptor_64) == 16);
78+
79+
struct MemoryListHeader {
80+
support::ulittle32_t NumberOfMemoryRanges;
81+
};
82+
static_assert(sizeof(MemoryListHeader) == 4);
83+
84+
struct Memory64ListHeader {
85+
support::ulittle64_t NumberOfMemoryRanges;
86+
support::ulittle64_t BaseRVA;
87+
};
88+
static_assert(sizeof(Memory64ListHeader) == 16);
7789

7890
struct MemoryInfoListHeader {
7991
support::ulittle32_t SizeOfHeader;

llvm/include/llvm/Object/Minidump.h

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "llvm/ADT/DenseMap.h"
1313
#include "llvm/ADT/StringExtras.h"
14+
#include "llvm/ADT/fallible_iterator.h"
1415
#include "llvm/ADT/iterator.h"
1516
#include "llvm/BinaryFormat/Minidump.h"
1617
#include "llvm/Object/Binary.h"
@@ -103,6 +104,13 @@ class MinidumpFile : public Binary {
103104
minidump::StreamType::MemoryList);
104105
}
105106

107+
/// Returns the header to the memory 64 list stream. An error is returned if
108+
/// the file does not contain this stream.
109+
Expected<minidump::Memory64ListHeader> getMemoryList64Header() const {
110+
return getStream<minidump::Memory64ListHeader>(
111+
minidump::StreamType::Memory64List);
112+
}
113+
106114
class MemoryInfoIterator
107115
: public iterator_facade_base<MemoryInfoIterator,
108116
std::forward_iterator_tag,
@@ -132,6 +140,90 @@ class MinidumpFile : public Binary {
132140
size_t Stride;
133141
};
134142

143+
/// Class the provides an iterator over the memory64 memory ranges. Only the
144+
/// the first descriptor is validated as readable beforehand.
145+
class Memory64Iterator {
146+
public:
147+
static Memory64Iterator
148+
begin(ArrayRef<uint8_t> Storage,
149+
ArrayRef<minidump::MemoryDescriptor_64> Descriptors) {
150+
return Memory64Iterator(Storage, Descriptors);
151+
}
152+
153+
static Memory64Iterator end() { return Memory64Iterator(); }
154+
155+
bool operator==(const Memory64Iterator &R) const {
156+
return IsEnd == R.IsEnd;
157+
}
158+
159+
bool operator!=(const Memory64Iterator &R) const { return !(*this == R); }
160+
161+
const std::pair<minidump::MemoryDescriptor_64, ArrayRef<uint8_t>> &
162+
operator*() {
163+
return Current;
164+
}
165+
166+
const std::pair<minidump::MemoryDescriptor_64, ArrayRef<uint8_t>> *
167+
operator->() {
168+
return &Current;
169+
}
170+
171+
Error inc() {
172+
if (Descriptors.empty()) {
173+
IsEnd = true;
174+
return Error::success();
175+
}
176+
177+
// Drop front gives us an array ref, so we need to call .front() as well.
178+
const minidump::MemoryDescriptor_64 &Descriptor = Descriptors.front();
179+
if (Descriptor.DataSize > Storage.size()) {
180+
IsEnd = true;
181+
return make_error<GenericBinaryError>(
182+
"Memory64 Descriptor exceeds end of file.",
183+
object_error::unexpected_eof);
184+
}
185+
186+
ArrayRef<uint8_t> Content = Storage.take_front(Descriptor.DataSize);
187+
Current = std::make_pair(Descriptor, Content);
188+
189+
Storage = Storage.drop_front(Descriptor.DataSize);
190+
Descriptors = Descriptors.drop_front();
191+
192+
return Error::success();
193+
}
194+
195+
private:
196+
// This constructor expects that the first descriptor is readable.
197+
Memory64Iterator(ArrayRef<uint8_t> Storage,
198+
ArrayRef<minidump::MemoryDescriptor_64> Descriptors)
199+
: Storage(Storage), Descriptors(Descriptors), IsEnd(false) {
200+
assert(!Descriptors.empty() &&
201+
Storage.size() >= Descriptors.front().DataSize);
202+
minidump::MemoryDescriptor_64 Descriptor = Descriptors.front();
203+
ArrayRef<uint8_t> Content = Storage.take_front(Descriptor.DataSize);
204+
Current = std::make_pair(Descriptor, Content);
205+
this->Descriptors = Descriptors.drop_front();
206+
this->Storage = Storage.drop_front(Descriptor.DataSize);
207+
}
208+
209+
Memory64Iterator()
210+
: Storage(ArrayRef<uint8_t>()),
211+
Descriptors(ArrayRef<minidump::MemoryDescriptor_64>()), IsEnd(true) {}
212+
213+
std::pair<minidump::MemoryDescriptor_64, ArrayRef<uint8_t>> Current;
214+
ArrayRef<uint8_t> Storage;
215+
ArrayRef<minidump::MemoryDescriptor_64> Descriptors;
216+
bool IsEnd;
217+
};
218+
219+
using FallibleMemory64Iterator = llvm::fallible_iterator<Memory64Iterator>;
220+
221+
/// Returns an iterator that pairs each descriptor with it's respective
222+
/// content from the Memory64List stream. An error is returned if the file
223+
/// does not contain a Memory64List stream, or if the descriptor data is
224+
/// unreadable.
225+
iterator_range<FallibleMemory64Iterator> getMemory64List(Error &Err) const;
226+
135227
/// Returns the list of descriptors embedded in the MemoryInfoList stream. The
136228
/// descriptors provide properties (e.g. permissions) of interesting regions
137229
/// of memory at the time the minidump was taken. An error is returned if the
@@ -152,15 +244,15 @@ class MinidumpFile : public Binary {
152244
}
153245

154246
/// Return a slice of the given data array, with bounds checking.
155-
static Expected<ArrayRef<uint8_t>> getDataSlice(ArrayRef<uint8_t> Data,
156-
size_t Offset, size_t Size);
247+
static Expected<ArrayRef<uint8_t>>
248+
getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size);
157249

158250
/// Return the slice of the given data array as an array of objects of the
159251
/// given type. The function checks that the input array is large enough to
160252
/// contain the correct number of objects of the given type.
161253
template <typename T>
162254
static Expected<ArrayRef<T>> getDataSliceAs(ArrayRef<uint8_t> Data,
163-
size_t Offset, size_t Count);
255+
uint64_t Offset, uint64_t Count);
164256

165257
MinidumpFile(MemoryBufferRef Source, const minidump::Header &Header,
166258
ArrayRef<minidump::Directory> Streams,
@@ -199,15 +291,16 @@ Expected<const T &> MinidumpFile::getStream(minidump::StreamType Type) const {
199291

200292
template <typename T>
201293
Expected<ArrayRef<T>> MinidumpFile::getDataSliceAs(ArrayRef<uint8_t> Data,
202-
size_t Offset,
203-
size_t Count) {
294+
uint64_t Offset,
295+
uint64_t Count) {
204296
// Check for overflow.
205-
if (Count > std::numeric_limits<size_t>::max() / sizeof(T))
297+
if (Count > std::numeric_limits<uint64_t>::max() / sizeof(T))
206298
return createEOFError();
207299
Expected<ArrayRef<uint8_t>> Slice =
208300
getDataSlice(Data, Offset, sizeof(T) * Count);
209301
if (!Slice)
210302
return Slice.takeError();
303+
211304
return ArrayRef<T>(reinterpret_cast<const T *>(Slice->data()), Count);
212305
}
213306

llvm/include/llvm/ObjectYAML/MinidumpYAML.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct Stream {
2929
Exception,
3030
MemoryInfoList,
3131
MemoryList,
32+
Memory64List,
3233
ModuleList,
3334
RawContent,
3435
SystemInfo,
@@ -98,12 +99,30 @@ struct ParsedMemoryDescriptor {
9899
minidump::MemoryDescriptor Entry;
99100
yaml::BinaryRef Content;
100101
};
102+
103+
struct ParsedMemory64Descriptor {
104+
static constexpr Stream::StreamKind Kind = Stream::StreamKind::Memory64List;
105+
static constexpr minidump::StreamType Type =
106+
minidump::StreamType::Memory64List;
107+
108+
minidump::MemoryDescriptor_64 Entry;
109+
yaml::BinaryRef Content;
110+
};
101111
} // namespace detail
102112

103113
using ModuleListStream = detail::ListStream<detail::ParsedModule>;
104114
using ThreadListStream = detail::ListStream<detail::ParsedThread>;
105115
using MemoryListStream = detail::ListStream<detail::ParsedMemoryDescriptor>;
106116

117+
struct Memory64ListStream
118+
: public detail::ListStream<detail::ParsedMemory64Descriptor> {
119+
minidump::Memory64ListHeader Header;
120+
121+
explicit Memory64ListStream(
122+
std::vector<detail::ParsedMemory64Descriptor> Entries = {})
123+
: ListStream(Entries) {}
124+
};
125+
107126
/// ExceptionStream minidump stream.
108127
struct ExceptionStream : public Stream {
109128
minidump::ExceptionStream MDExceptionStream;
@@ -244,6 +263,12 @@ template <> struct MappingContextTraits<minidump::MemoryDescriptor, BinaryRef> {
244263
BinaryRef &Content);
245264
};
246265

266+
template <>
267+
struct MappingContextTraits<minidump::MemoryDescriptor_64, BinaryRef> {
268+
static void mapping(IO &IO, minidump::MemoryDescriptor_64 &Memory,
269+
BinaryRef &Content);
270+
};
271+
247272
} // namespace yaml
248273

249274
} // namespace llvm
@@ -269,11 +294,14 @@ LLVM_YAML_DECLARE_MAPPING_TRAITS(
269294
llvm::MinidumpYAML::ModuleListStream::entry_type)
270295
LLVM_YAML_DECLARE_MAPPING_TRAITS(
271296
llvm::MinidumpYAML::ThreadListStream::entry_type)
297+
LLVM_YAML_DECLARE_MAPPING_TRAITS(
298+
llvm::MinidumpYAML::Memory64ListStream::entry_type)
272299

273300
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<llvm::MinidumpYAML::Stream>)
274301
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::MemoryListStream::entry_type)
275302
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::entry_type)
276303
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ThreadListStream::entry_type)
304+
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::Memory64ListStream::entry_type)
277305
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::minidump::MemoryInfo)
278306

279307
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object)

llvm/lib/Object/Minidump.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ template Expected<ArrayRef<Thread>>
9999
template Expected<ArrayRef<MemoryDescriptor>>
100100
MinidumpFile::getListStream(StreamType) const;
101101

102-
Expected<ArrayRef<uint8_t>>
103-
MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) {
102+
Expected<ArrayRef<uint8_t>> MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data,
103+
uint64_t Offset,
104+
uint64_t Size) {
104105
// Check for overflow.
105106
if (Offset + Size < Offset || Offset + Size < Size ||
106107
Offset + Size > Data.size())
@@ -154,3 +155,43 @@ MinidumpFile::create(MemoryBufferRef Source) {
154155
return std::unique_ptr<MinidumpFile>(
155156
new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap)));
156157
}
158+
159+
iterator_range<MinidumpFile::FallibleMemory64Iterator>
160+
MinidumpFile::getMemory64List(Error &Err) const {
161+
ErrorAsOutParameter ErrAsOutParam(&Err);
162+
auto end = FallibleMemory64Iterator::end(Memory64Iterator::end());
163+
Expected<minidump::Memory64ListHeader> ListHeader = getMemoryList64Header();
164+
if (!ListHeader) {
165+
Err = ListHeader.takeError();
166+
return make_range(end, end);
167+
}
168+
169+
std::optional<ArrayRef<uint8_t>> Stream =
170+
getRawStream(StreamType::Memory64List);
171+
if (!Stream) {
172+
Err = createError("No such stream");
173+
return make_range(end, end);
174+
}
175+
176+
Expected<ArrayRef<minidump::MemoryDescriptor_64>> Descriptors =
177+
getDataSliceAs<minidump::MemoryDescriptor_64>(
178+
*Stream, sizeof(Memory64ListHeader),
179+
ListHeader->NumberOfMemoryRanges);
180+
181+
if (!Descriptors) {
182+
Err = Descriptors.takeError();
183+
return make_range(end, end);
184+
}
185+
186+
if (!Descriptors->empty() &&
187+
ListHeader->BaseRVA + Descriptors->front().DataSize > getData().size()) {
188+
Err = createError("Memory64List header RVA out of range");
189+
return make_range(end, end);
190+
}
191+
192+
return make_range(FallibleMemory64Iterator::itr(
193+
Memory64Iterator::begin(
194+
getData().slice(ListHeader->BaseRVA), *Descriptors),
195+
Err),
196+
FallibleMemory64Iterator::end(Memory64Iterator::end()));
197+
}

llvm/lib/ObjectYAML/MinidumpEmitter.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,30 @@ static size_t layout(BlobAllocator &File, MinidumpYAML::ExceptionStream &S) {
136136
return DataEnd;
137137
}
138138

139+
static size_t layout(BlobAllocator &File, MinidumpYAML::Memory64ListStream &S) {
140+
size_t BaseRVA = File.tell() + sizeof(minidump::Memory64ListHeader);
141+
BaseRVA += S.Entries.size() * sizeof(minidump::MemoryDescriptor_64);
142+
S.Header.BaseRVA = BaseRVA;
143+
S.Header.NumberOfMemoryRanges = S.Entries.size();
144+
File.allocateObject(S.Header);
145+
for (auto &E : S.Entries)
146+
File.allocateObject(E.Entry);
147+
148+
// Save the new offset for the stream size.
149+
size_t DataEnd = File.tell();
150+
for (auto &E : S.Entries) {
151+
File.allocateBytes(E.Content);
152+
if (E.Entry.DataSize > E.Content.binary_size()) {
153+
size_t Padding = E.Entry.DataSize - E.Content.binary_size();
154+
File.allocateCallback(Padding, [Padding](raw_ostream &OS) {
155+
OS << std::string(Padding, '\0');
156+
});
157+
}
158+
}
159+
160+
return DataEnd;
161+
}
162+
139163
static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) {
140164
Range.Entry.Memory = layout(File, Range.Content);
141165
}
@@ -190,6 +214,9 @@ static Directory layout(BlobAllocator &File, Stream &S) {
190214
case Stream::StreamKind::MemoryList:
191215
DataEnd = layout(File, cast<MemoryListStream>(S));
192216
break;
217+
case Stream::StreamKind::Memory64List:
218+
DataEnd = layout(File, cast<Memory64ListStream>(S));
219+
break;
193220
case Stream::StreamKind::ModuleList:
194221
DataEnd = layout(File, cast<ModuleListStream>(S));
195222
break;

0 commit comments

Comments
 (0)