Index: llvm/trunk/include/llvm/Object/Minidump.h =================================================================== --- llvm/trunk/include/llvm/Object/Minidump.h +++ llvm/trunk/include/llvm/Object/Minidump.h @@ -43,6 +43,10 @@ /// file does not contain a stream of this type. Optional> getRawStream(minidump::StreamType Type) const; + /// Returns the minidump string at the given offset. An error is returned if + /// we fail to parse the string, or the string is invalid UTF16. + Expected getString(size_t Offset) const; + /// Returns the contents of the SystemInfo stream, cast to the appropriate /// type. An error is returned if the file does not contain this stream, or /// the stream is smaller than the size of the SystemInfo structure. The Index: llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h @@ -67,10 +67,12 @@ /// SystemInfo minidump stream. struct SystemInfoStream : public Stream { minidump::SystemInfo Info; + std::string CSDVersion; - explicit SystemInfoStream(const minidump::SystemInfo &Info) + explicit SystemInfoStream(const minidump::SystemInfo &Info, + std::string CSDVersion) : Stream(StreamKind::SystemInfo, minidump::StreamType::SystemInfo), - Info(Info) {} + Info(Info), CSDVersion(std::move(CSDVersion)) {} SystemInfoStream() : Stream(StreamKind::SystemInfo, minidump::StreamType::SystemInfo) { Index: llvm/trunk/lib/Object/Minidump.cpp =================================================================== --- llvm/trunk/lib/Object/Minidump.cpp +++ llvm/trunk/lib/Object/Minidump.cpp @@ -8,6 +8,7 @@ #include "llvm/Object/Minidump.h" #include "llvm/Object/Error.h" +#include "llvm/Support/ConvertUTF.h" using namespace llvm; using namespace llvm::object; @@ -21,6 +22,33 @@ return None; } +Expected MinidumpFile::getString(size_t Offset) const { + // Minidump strings consist of a 32-bit length field, which gives the size of + // the string in *bytes*. This is followed by the actual string encoded in + // UTF16. + auto ExpectedSize = + getDataSliceAs(getData(), Offset, 1); + if (!ExpectedSize) + return ExpectedSize.takeError(); + size_t Size = (*ExpectedSize)[0]; + if (Size % 2 != 0) + return createError("String size not even"); + Size /= 2; + if (Size == 0) + return ""; + + Offset += sizeof(support::ulittle32_t); + auto ExpectedData = getDataSliceAs(getData(), Offset, Size); + if (!ExpectedData) + return ExpectedData.takeError(); + + std::string Result; + if (!convertUTF16ToUTF8String(*ExpectedData, Result)) + return createError("String decoding failed"); + + return Result; +} + Expected> MinidumpFile::getDataSlice(ArrayRef Data, size_t Offset, size_t Size) { // Check for overflow. Index: llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp +++ llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ObjectYAML/MinidumpYAML.h" +#include "llvm/Support/ConvertUTF.h" using namespace llvm; using namespace llvm::MinidumpYAML; @@ -39,6 +40,8 @@ return allocateArray(makeArrayRef(Data)); } + size_t allocateString(StringRef Str); + void writeTo(raw_ostream &OS) const; private: @@ -48,6 +51,26 @@ }; } // namespace +size_t BlobAllocator::allocateString(StringRef Str) { + SmallVector WStr; + bool OK = convertUTF8ToUTF16String(Str, WStr); + assert(OK && "Invalid UTF8 in Str?"); + (void)OK; + + SmallVector EndianStr(WStr.size() + 1, + support::ulittle16_t()); + copy(WStr, EndianStr.begin()); + return allocateCallback( + sizeof(uint32_t) + EndianStr.size() * sizeof(support::ulittle16_t), + [EndianStr](raw_ostream &OS) { + // Length does not include the null-terminator. + support::ulittle32_t Length(2 * (EndianStr.size() - 1)); + OS.write(reinterpret_cast(&Length), sizeof(Length)); + OS.write(reinterpret_cast(EndianStr.begin()), + sizeof(support::ulittle16_t) * EndianStr.size()); + }); +} + void BlobAllocator::writeTo(raw_ostream &OS) const { size_t BeginOffset = OS.tell(); for (const auto &Callback : Callbacks) @@ -269,7 +292,7 @@ mapOptional(IO, "Minor Version", Info.MinorVersion, 0); mapOptional(IO, "Build Number", Info.BuildNumber, 0); IO.mapRequired("Platform ID", Info.PlatformId); - mapOptionalHex(IO, "CSD Version RVA", Info.CSDVersionRVA, 0); + IO.mapOptional("CSD Version", Stream.CSDVersion, ""); mapOptionalHex(IO, "Suite Mask", Info.SuiteMask, 0); mapOptionalHex(IO, "Reserved", Info.Reserved, 0); switch (static_cast(Info.ProcessorArch)) { @@ -337,6 +360,7 @@ Directory Result; Result.Type = S.Type; Result.Location.RVA = File.tell(); + Optional DataEnd; switch (S.Kind) { case Stream::StreamKind::RawContent: { RawContentStream &Raw = cast(S); @@ -347,14 +371,22 @@ }); break; } - case Stream::StreamKind::SystemInfo: - File.allocateObject(cast(S).Info); + case Stream::StreamKind::SystemInfo: { + SystemInfoStream &SystemInfo = cast(S); + File.allocateObject(SystemInfo.Info); + // The CSD string is not a part of the stream. + DataEnd = File.tell(); + SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion); break; + } case Stream::StreamKind::TextContent: File.allocateArray(arrayRefFromStringRef(cast(S).Text)); break; } - Result.Location.DataSize = File.tell() - Result.Location.RVA; + // If DataEnd is not set, we assume everything we generated is a part of the + // stream. + Result.Location.DataSize = + DataEnd.getValueOr(File.tell()) - Result.Location.RVA; return Result; } @@ -395,7 +427,11 @@ auto ExpectedInfo = File.getSystemInfo(); if (!ExpectedInfo) return ExpectedInfo.takeError(); - return make_unique(*ExpectedInfo); + auto ExpectedCSDVersion = File.getString(ExpectedInfo->CSDVersionRVA); + if (!ExpectedCSDVersion) + return ExpectedInfo.takeError(); + return make_unique(*ExpectedInfo, + std::move(*ExpectedCSDVersion)); } case StreamKind::TextContent: return make_unique( Index: llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml =================================================================== --- llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml +++ llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml @@ -5,7 +5,7 @@ - Type: SystemInfo Processor Arch: ARM64 Platform ID: Linux - CSD Version RVA: 0x01020304 + CSD Version: Linux 3.13.0-91-generic CPU: CPUID: 0x05060708 - Type: LinuxAuxv @@ -22,7 +22,7 @@ # CHECK-NEXT: - Type: SystemInfo # CHECK-NEXT: Processor Arch: ARM64 # CHECK-NEXT: Platform ID: Linux -# CHECK-NEXT: CSD Version RVA: 0x01020304 +# CHECK-NEXT: CSD Version: Linux 3.13.0-91-generic # CHECK-NEXT: CPU: # CHECK-NEXT: CPUID: 0x05060708 # CHECK-NEXT: - Type: LinuxAuxv Index: llvm/trunk/unittests/Object/MinidumpTest.cpp =================================================================== --- llvm/trunk/unittests/Object/MinidumpTest.cpp +++ llvm/trunk/unittests/Object/MinidumpTest.cpp @@ -249,3 +249,38 @@ EXPECT_EQ(0x08070605u, Info.CPU.X86.FeatureInfo); EXPECT_EQ(0x02010009u, Info.CPU.X86.AMDExtendedFeatures); } + +TEST(MinidumpFile, getString) { + std::vector ManyStrings{ + // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 2, 0, 0, 0, // NumberOfStreams, + 0x20, 0, 0, 0, // StreamDirectoryRVA + 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, + 0x20, 0, 0, 0, // RVA + 1, 0, 0, 0, 0, 0, // String1 - odd length + 0, 0, 1, 0, 0, 0, // String2 - too long + 2, 0, 0, 0, 0, 0xd8, // String3 - invalid utf16 + 0, 0, 0, 0, 0, 0, // String4 - "" + 2, 0, 0, 0, 'a', 0, // String5 - "a" + 0, // Mis-align next string + 2, 0, 0, 0, 'a', 0, // String6 - "a" + + }; + auto ExpectedFile = create(ManyStrings); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + EXPECT_THAT_EXPECTED(File.getString(44), Failed()); + EXPECT_THAT_EXPECTED(File.getString(50), Failed()); + EXPECT_THAT_EXPECTED(File.getString(56), Failed()); + EXPECT_THAT_EXPECTED(File.getString(62), HasValue("")); + EXPECT_THAT_EXPECTED(File.getString(68), HasValue("a")); + EXPECT_THAT_EXPECTED(File.getString(75), HasValue("a")); + + // Check the case when the size field does not fit into the remaining data. + EXPECT_THAT_EXPECTED(File.getString(ManyStrings.size() - 2), + Failed()); +} Index: llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp =================================================================== --- llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp +++ llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp @@ -33,7 +33,6 @@ - Type: SystemInfo Processor Arch: ARM64 Platform ID: Linux - CSD Version RVA: 0x01020304 CPU: CPUID: 0x05060708 - Type: LinuxMaps @@ -54,7 +53,6 @@ const SystemInfo &SysInfo = *ExpectedSysInfo; EXPECT_EQ(ProcessorArchitecture::ARM64, SysInfo.ProcessorArch); EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); - EXPECT_EQ(0x01020304u, SysInfo.CSDVersionRVA); EXPECT_EQ(0x05060708u, SysInfo.CPU.Arm.CPUID); EXPECT_EQ(StreamType::LinuxMaps, File.streams()[1].Type);