Index: include/llvm/BinaryFormat/Minidump.h =================================================================== --- include/llvm/BinaryFormat/Minidump.h +++ include/llvm/BinaryFormat/Minidump.h @@ -124,6 +124,37 @@ }; 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 { Index: include/llvm/Object/Minidump.h =================================================================== --- include/llvm/Object/Minidump.h +++ include/llvm/Object/Minidump.h @@ -53,6 +53,13 @@ return getStream(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> getModuleList() const; + private: static Error createError(StringRef Str, object_error Err = object_error::parse_failed) { Index: lib/Object/Minidump.cpp =================================================================== --- lib/Object/Minidump.cpp +++ lib/Object/Minidump.cpp @@ -49,6 +49,27 @@ return Result; } +Expected> MinidumpFile::getModuleList() const { + auto OptionalStream = getRawStream(StreamType::ModuleList); + if (!OptionalStream) + return createError("No such stream", object_error::invalid_section_index); + auto ExpectedSize = + getDataSliceAs(*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(*OptionalStream, ListOffset, ListSize); +} + Expected> MinidumpFile::getDataSlice(ArrayRef Data, size_t Offset, size_t Size) { // Check for overflow. Index: unittests/Object/MinidumpTest.cpp =================================================================== --- unittests/Object/MinidumpTest.cpp +++ unittests/Object/MinidumpTest.cpp @@ -280,3 +280,115 @@ EXPECT_THAT_EXPECTED(File.getString(68), HasValue("a")); EXPECT_THAT_EXPECTED(File.getString(75), HasValue("a")); } + +TEST(MinidumpFile, getModuleList) { + std::vector OneModule{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 0x20, 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, + 0x2c, 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 PaddedModule{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 0x20, 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, + 0x2c, 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 &Data : {OneModule, PaddedModule}) { + auto ExpectedFile = create(Data); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + Expected> 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 StreamTooShort{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 0x20, 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, + 0x2c, 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()); +}