diff options
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/include/llvm/BinaryFormat/Minidump.h | 31 | ||||
-rw-r--r-- | llvm/include/llvm/Object/Minidump.h | 19 | ||||
-rw-r--r-- | llvm/lib/Object/Minidump.cpp | 21 | ||||
-rw-r--r-- | llvm/unittests/Object/MinidumpTest.cpp | 112 |
4 files changed, 177 insertions, 6 deletions
diff --git a/llvm/include/llvm/BinaryFormat/Minidump.h b/llvm/include/llvm/BinaryFormat/Minidump.h index 1e2dd972830..2bb75a6cc1f 100644 --- a/llvm/include/llvm/BinaryFormat/Minidump.h +++ b/llvm/include/llvm/BinaryFormat/Minidump.h @@ -124,6 +124,37 @@ struct SystemInfo { }; static_assert(sizeof(SystemInfo) == 56, ""); +struct VSFixedFileInfo { + support::ulittle32_t Signature; + support::ulittle32_t StructVersion; + support::ulittle32_t FileVersionHigh; + support::ulittle32_t FileVersionLow; + support::ulittle32_t ProductVersionHigh; + support::ulittle32_t ProductVersionLow; + support::ulittle32_t FileFlagsMask; + support::ulittle32_t FileFlags; + support::ulittle32_t FileOS; + support::ulittle32_t FileType; + support::ulittle32_t FileSubtype; + support::ulittle32_t FileDateHigh; + support::ulittle32_t FileDateLow; +}; +static_assert(sizeof(VSFixedFileInfo) == 52, ""); + +struct Module { + support::ulittle64_t BaseOfImage; + support::ulittle32_t SizeOfImage; + support::ulittle32_t Checksum; + support::ulittle32_t TimeDateStamp; + support::ulittle32_t ModuleNameRVA; + VSFixedFileInfo VersionInfo; + LocationDescriptor CvRecord; + LocationDescriptor MiscRecord; + support::ulittle64_t Reserved0; + support::ulittle64_t Reserved1; +}; +static_assert(sizeof(Module) == 108, ""); + } // namespace minidump template <> struct DenseMapInfo<minidump::StreamType> { diff --git a/llvm/include/llvm/Object/Minidump.h b/llvm/include/llvm/Object/Minidump.h index 63a43d5bd88..0a74fcb4354 100644 --- a/llvm/include/llvm/Object/Minidump.h +++ b/llvm/include/llvm/Object/Minidump.h @@ -55,14 +55,21 @@ public: return getStream<minidump::SystemInfo>(minidump::StreamType::SystemInfo); } + /// Returns the module list embedded in the ModuleList stream. An error is + /// returned if the file does not contain this stream, or if the stream is + /// not large enough to contain the number of modules declared in the stream + /// header. The consistency of the Module entries themselves is not checked in + /// any way. + Expected<ArrayRef<minidump::Module>> getModuleList() const; + private: - static Error createError(StringRef Str, - object_error Err = object_error::parse_failed) { - return make_error<GenericBinaryError>(Str, Err); + static Error createError(StringRef Str) { + return make_error<GenericBinaryError>(Str, object_error::parse_failed); } static Error createEOFError() { - return createError("Unexpected EOF", object_error::unexpected_eof); + return make_error<GenericBinaryError>("Unexpected EOF", + object_error::unexpected_eof); } /// Return a slice of the given data array, with bounds checking. @@ -101,9 +108,9 @@ Expected<const T &> MinidumpFile::getStream(minidump::StreamType Stream) const { if (auto OptionalStream = getRawStream(Stream)) { if (OptionalStream->size() >= sizeof(T)) return *reinterpret_cast<const T *>(OptionalStream->data()); - return createError("Malformed stream", object_error::unexpected_eof); + return createEOFError(); } - return createError("No such stream", object_error::invalid_section_index); + return createError("No such stream"); } template <typename T> diff --git a/llvm/lib/Object/Minidump.cpp b/llvm/lib/Object/Minidump.cpp index 1a22491ce3c..4b1abe52994 100644 --- a/llvm/lib/Object/Minidump.cpp +++ b/llvm/lib/Object/Minidump.cpp @@ -53,6 +53,27 @@ Expected<std::string> MinidumpFile::getString(size_t Offset) const { return Result; } +Expected<ArrayRef<Module>> MinidumpFile::getModuleList() const { + auto OptionalStream = getRawStream(StreamType::ModuleList); + if (!OptionalStream) + return createError("No such stream"); + auto ExpectedSize = + getDataSliceAs<support::ulittle32_t>(*OptionalStream, 0, 1); + if (!ExpectedSize) + return ExpectedSize.takeError(); + + size_t ListSize = ExpectedSize.get()[0]; + + size_t ListOffset = 4; + // Some producers insert additional padding bytes to align the module list to + // 8-byte boundary. Check for that by comparing the module list size with the + // overall stream size. + if (ListOffset + sizeof(Module) * ListSize < OptionalStream->size()) + ListOffset = 8; + + return getDataSliceAs<Module>(*OptionalStream, ListOffset, ListSize); +} + Expected<ArrayRef<uint8_t>> MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) { // Check for overflow. diff --git a/llvm/unittests/Object/MinidumpTest.cpp b/llvm/unittests/Object/MinidumpTest.cpp index 06034e1e5ad..fdb9d22db04 100644 --- a/llvm/unittests/Object/MinidumpTest.cpp +++ b/llvm/unittests/Object/MinidumpTest.cpp @@ -284,3 +284,115 @@ TEST(MinidumpFile, getString) { EXPECT_THAT_EXPECTED(File.getString(ManyStrings.size() - 2), Failed<BinaryError>()); } + +TEST(MinidumpFile, getModuleList) { + std::vector<uint8_t> OneModule{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 32, 0, 0, 0, // StreamDirectoryRVA + 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp + 0, 0, 0, 0, 0, 0, 0, 0, // Flags + // Stream Directory + 4, 0, 0, 0, 112, 0, 0, 0, // Type, DataSize, + 44, 0, 0, 0, // RVA + // ModuleList + 1, 0, 0, 0, // NumberOfModules + 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage + 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum + 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA + 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion + 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion + 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion + 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags + 0, 0, 0, 0, // FileOS + 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType + 0, 0, 0, 0, 0, 0, 0, 0, // FileDate + 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord + 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord + 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 + 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 + }; + // Same as before, but with a padded module list. + std::vector<uint8_t> PaddedModule{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 32, 0, 0, 0, // StreamDirectoryRVA + 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp + 0, 0, 0, 0, 0, 0, 0, 0, // Flags + // Stream Directory + 4, 0, 0, 0, 116, 0, 0, 0, // Type, DataSize, + 44, 0, 0, 0, // RVA + // ModuleList + 1, 0, 0, 0, // NumberOfModules + 0, 0, 0, 0, // Padding + 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage + 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum + 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA + 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion + 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion + 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion + 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags + 0, 0, 0, 0, // FileOS + 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType + 0, 0, 0, 0, 0, 0, 0, 0, // FileDate + 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord + 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord + 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 + 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 + }; + + for (const std::vector<uint8_t> &Data : {OneModule, PaddedModule}) { + auto ExpectedFile = create(Data); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + Expected<ArrayRef<Module>> ExpectedModule = File.getModuleList(); + ASSERT_THAT_EXPECTED(ExpectedModule, Succeeded()); + ASSERT_EQ(1u, ExpectedModule->size()); + const Module &M = ExpectedModule.get()[0]; + EXPECT_EQ(0x0807060504030201u, M.BaseOfImage); + EXPECT_EQ(0x02010009u, M.SizeOfImage); + EXPECT_EQ(0x06050403u, M.Checksum); + EXPECT_EQ(0x00090807u, M.TimeDateStamp); + EXPECT_EQ(0x04030201u, M.ModuleNameRVA); + EXPECT_EQ(0x04030201u, M.CvRecord.DataSize); + EXPECT_EQ(0x08070605u, M.CvRecord.RVA); + EXPECT_EQ(0x02010009u, M.MiscRecord.DataSize); + EXPECT_EQ(0x06050403u, M.MiscRecord.RVA); + EXPECT_EQ(0x0403020100090807u, M.Reserved0); + EXPECT_EQ(0x0201000908070605u, M.Reserved1); + } + + std::vector<uint8_t> StreamTooShort{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 32, 0, 0, 0, // StreamDirectoryRVA + 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp + 0, 0, 0, 0, 0, 0, 0, 0, // Flags + // Stream Directory + 4, 0, 0, 0, 111, 0, 0, 0, // Type, DataSize, + 44, 0, 0, 0, // RVA + // ModuleList + 1, 0, 0, 0, // NumberOfModules + 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage + 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum + 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA + 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion + 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion + 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion + 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags + 0, 0, 0, 0, // FileOS + 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType + 0, 0, 0, 0, 0, 0, 0, 0, // FileDate + 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord + 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord + 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 + 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 + }; + auto ExpectedFile = create(StreamTooShort); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + EXPECT_THAT_EXPECTED(File.getModuleList(), Failed<BinaryError>()); +} |