Index: llvm/trunk/include/llvm/BinaryFormat/Magic.h =================================================================== --- llvm/trunk/include/llvm/BinaryFormat/Magic.h +++ llvm/trunk/include/llvm/BinaryFormat/Magic.h @@ -39,6 +39,7 @@ macho_dsym_companion, ///< Mach-O dSYM companion file macho_kext_bundle, ///< Mach-O kext bundle file macho_universal_binary, ///< Mach-O universal binary + minidump, ///< Windows minidump file coff_cl_gl_object, ///< Microsoft cl.exe's intermediate code file coff_object, ///< COFF object file coff_import_library, ///< COFF import library Index: llvm/trunk/include/llvm/BinaryFormat/Minidump.h =================================================================== --- llvm/trunk/include/llvm/BinaryFormat/Minidump.h +++ llvm/trunk/include/llvm/BinaryFormat/Minidump.h @@ -0,0 +1,147 @@ +//===- Minidump.h - Minidump constants and structures -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header constants and data structures pertaining to the Windows Minidump +// core file format. +// +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx +// https://chromium.googlesource.com/breakpad/breakpad/ +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_MINIDUMP_H +#define LLVM_BINARYFORMAT_MINIDUMP_H + +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace minidump { + +/// The minidump header is the first part of a minidump file. It identifies the +/// file as a minidump file, and gives the location of the stream directory. +struct Header { + static constexpr uint32_t MagicSignature = 0x504d444d; // PMDM + static constexpr uint16_t MagicVersion = 0xa793; + + support::ulittle32_t Signature; + // The high 16 bits of version field are implementation specific. The low 16 + // bits should be MagicVersion. + support::ulittle32_t Version; + support::ulittle32_t NumberOfStreams; + support::ulittle32_t StreamDirectoryRVA; + support::ulittle32_t Checksum; + support::ulittle32_t TimeDateStamp; + support::ulittle64_t Flags; +}; +static_assert(sizeof(Header) == 32, ""); + +/// The type of a minidump stream identifies its contents. Streams numbers after +/// LastReserved are for application-defined data streams. +enum class StreamType : uint32_t { +#define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) NAME = CODE, +#include "llvm/BinaryFormat/MinidumpConstants.def" + Unused = 0, + LastReserved = 0x0000ffff, +}; + +/// Specifies the location (and size) of various objects in the minidump file. +/// The location is relative to the start of the file. +struct LocationDescriptor { + support::ulittle32_t DataSize; + support::ulittle32_t RVA; +}; +static_assert(sizeof(LocationDescriptor) == 8, ""); + +/// Specifies the location and type of a single stream in the minidump file. The +/// minidump stream directory is an array of entries of this type, with its size +/// given by Header.NumberOfStreams. +struct Directory { + support::little_t Type; + LocationDescriptor Location; +}; +static_assert(sizeof(Directory) == 12, ""); + +/// The processor architecture of the system that generated this minidump. Used +/// in the ProcessorArch field of the SystemInfo stream. +enum class ProcessorArchitecture : uint16_t { +#define HANDLE_MDMP_ARCH(CODE, NAME) NAME = CODE, +#include "llvm/BinaryFormat/MinidumpConstants.def" +}; + +/// The OS Platform of the system that generated this minidump. Used in the +/// PlatformId field of the SystemInfo stream. +enum class OSPlatform : uint32_t { +#define HANDLE_MDMP_PLATFORM(CODE, NAME) NAME = CODE, +#include "llvm/BinaryFormat/MinidumpConstants.def" +}; + +/// Detailed information about the processor of the system that generated this +/// minidump. Its interpretation depends on the ProcessorArchitecture enum. +union CPUInfo { + struct X86Info { + char VendorID[12]; // cpuid 0: ebx, edx, ecx + support::ulittle32_t VersionInfo; // cpuid 1: eax + support::ulittle32_t FeatureInfo; // cpuid 1: edx + support::ulittle32_t AMDExtendedFeatures; // cpuid 0x80000001, ebx + } X86; + struct ArmInfo { + support::ulittle32_t CPUID; + support::ulittle32_t ElfHWCaps; // linux specific, 0 otherwise + } Arm; + struct OtherInfo { + uint8_t ProcessorFeatures[16]; + } Other; +}; +static_assert(sizeof(CPUInfo) == 24, ""); + +/// The SystemInfo stream, containing various information about the system where +/// this minidump was generated. +struct SystemInfo { + support::little_t ProcessorArch; + support::ulittle16_t ProcessorLevel; + support::ulittle16_t ProcessorRevision; + + uint8_t NumberOfProcessors; + uint8_t ProductType; + + support::ulittle32_t MajorVersion; + support::ulittle32_t MinorVersion; + support::ulittle32_t BuildNumber; + support::little_t PlatformId; + support::ulittle32_t CSDVersionRVA; + + support::ulittle16_t SuiteMask; + support::ulittle16_t Reserved; + + CPUInfo CPU; +}; +static_assert(sizeof(SystemInfo) == 56, ""); + +} // namespace minidump + +template <> struct DenseMapInfo { + static minidump::StreamType getEmptyKey() { return minidump::StreamType(-1); } + + static minidump::StreamType getTombstoneKey() { + return minidump::StreamType(-2); + } + + static unsigned getHashValue(minidump::StreamType Val) { + return DenseMapInfo::getHashValue(static_cast(Val)); + } + + static bool isEqual(minidump::StreamType LHS, minidump::StreamType RHS) { + return LHS == RHS; + } +}; + +} // namespace llvm + +#endif // LLVM_BINARYFORMAT_MINIDUMP_H Index: llvm/trunk/include/llvm/BinaryFormat/MinidumpConstants.def =================================================================== --- llvm/trunk/include/llvm/BinaryFormat/MinidumpConstants.def +++ llvm/trunk/include/llvm/BinaryFormat/MinidumpConstants.def @@ -0,0 +1,107 @@ +//===- MinidumpConstants.def - Iteration over minidump constants-*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if !(defined HANDLE_MDMP_STREAM_TYPE || defined HANDLE_MDMP_ARCH || \ + defined HANDLE_MDMP_PLATFORM) +#error "Missing HANDLE_MDMP definition" +#endif + +#ifndef HANDLE_MDMP_STREAM_TYPE +#define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) +#endif + +#ifndef HANDLE_MDMP_ARCH +#define HANDLE_MDMP_ARCH(CODE, NAME) +#endif + +#ifndef HANDLE_MDMP_PLATFORM +#define HANDLE_MDMP_PLATFORM(CODE, NAME) +#endif + +HANDLE_MDMP_STREAM_TYPE(0x0003, ThreadList) +HANDLE_MDMP_STREAM_TYPE(0x0004, ModuleList) +HANDLE_MDMP_STREAM_TYPE(0x0005, MemoryList) +HANDLE_MDMP_STREAM_TYPE(0x0006, Exception) +HANDLE_MDMP_STREAM_TYPE(0x0007, SystemInfo) +HANDLE_MDMP_STREAM_TYPE(0x0008, ThreadExList) +HANDLE_MDMP_STREAM_TYPE(0x0009, Memory64List) +HANDLE_MDMP_STREAM_TYPE(0x000a, CommentA) +HANDLE_MDMP_STREAM_TYPE(0x000b, CommentW) +HANDLE_MDMP_STREAM_TYPE(0x000c, HandleData) +HANDLE_MDMP_STREAM_TYPE(0x000d, FunctionTable) +HANDLE_MDMP_STREAM_TYPE(0x000e, UnloadedModuleList) +HANDLE_MDMP_STREAM_TYPE(0x000f, MiscInfo) +HANDLE_MDMP_STREAM_TYPE(0x0010, MemoryInfoList) +HANDLE_MDMP_STREAM_TYPE(0x0011, ThreadInfoList) +HANDLE_MDMP_STREAM_TYPE(0x0012, HandleOperationList) +HANDLE_MDMP_STREAM_TYPE(0x0013, Token) +HANDLE_MDMP_STREAM_TYPE(0x0014, JavascriptData) +HANDLE_MDMP_STREAM_TYPE(0x0015, SystemMemoryInfo) +HANDLE_MDMP_STREAM_TYPE(0x0016, ProcessVMCounters) +// Breakpad extension types. 0x4767 = "Gg" +HANDLE_MDMP_STREAM_TYPE(0x47670001, BreakpadInfo) +HANDLE_MDMP_STREAM_TYPE(0x47670002, AssertionInfo) +// These are additional minidump stream values which are specific to the linux +// breakpad implementation. +HANDLE_MDMP_STREAM_TYPE(0x47670003, LinuxCPUInfo) // /proc/cpuinfo +HANDLE_MDMP_STREAM_TYPE(0x47670004, LinuxProcStatus) // /proc/$x/status +HANDLE_MDMP_STREAM_TYPE(0x47670005, LinuxLSBRelease) // /etc/lsb-release +HANDLE_MDMP_STREAM_TYPE(0x47670006, LinuxCMDLine) // /proc/$x/cmdline +HANDLE_MDMP_STREAM_TYPE(0x47670007, LinuxEnviron) // /proc/$x/environ +HANDLE_MDMP_STREAM_TYPE(0x47670008, LinuxAuxv) // /proc/$x/auxv +HANDLE_MDMP_STREAM_TYPE(0x47670009, LinuxMaps) // /proc/$x/maps +HANDLE_MDMP_STREAM_TYPE(0x4767000A, LinuxDSODebug) +HANDLE_MDMP_STREAM_TYPE(0x4767000B, LinuxProcStat) // /proc/$x/stat +HANDLE_MDMP_STREAM_TYPE(0x4767000C, LinuxProcUptime) // uptime +HANDLE_MDMP_STREAM_TYPE(0x4767000D, LinuxProcFD) // /proc/$x/fd +// Facebook-defined stream types +HANDLE_MDMP_STREAM_TYPE(0xFACE1CA7, FacebookLogcat) +HANDLE_MDMP_STREAM_TYPE(0xFACECAFA, FacebookAppCustomData) +HANDLE_MDMP_STREAM_TYPE(0xFACECAFB, FacebookBuildID) +HANDLE_MDMP_STREAM_TYPE(0xFACECAFC, FacebookAppVersionName) +HANDLE_MDMP_STREAM_TYPE(0xFACECAFD, FacebookJavaStack) +HANDLE_MDMP_STREAM_TYPE(0xFACECAFE, FacebookDalvikInfo) +HANDLE_MDMP_STREAM_TYPE(0xFACECAFF, FacebookUnwindSymbols) +HANDLE_MDMP_STREAM_TYPE(0xFACECB00, FacebookDumpErrorLog) +HANDLE_MDMP_STREAM_TYPE(0xFACECCCC, FacebookAppStateLog) +HANDLE_MDMP_STREAM_TYPE(0xFACEDEAD, FacebookAbortReason) +HANDLE_MDMP_STREAM_TYPE(0xFACEE000, FacebookThreadName) + +HANDLE_MDMP_ARCH(0x0000, X86) // PROCESSOR_ARCHITECTURE_INTEL +HANDLE_MDMP_ARCH(0x0001, MIPS) // PROCESSOR_ARCHITECTURE_MIPS +HANDLE_MDMP_ARCH(0x0002, Alpha) // PROCESSOR_ARCHITECTURE_ALPHA +HANDLE_MDMP_ARCH(0x0003, PPC) // PROCESSOR_ARCHITECTURE_PPC +HANDLE_MDMP_ARCH(0x0004, SHX) // PROCESSOR_ARCHITECTURE_SHX (Super-H) +HANDLE_MDMP_ARCH(0x0005, ARM) // PROCESSOR_ARCHITECTURE_ARM +HANDLE_MDMP_ARCH(0x0006, IA64) // PROCESSOR_ARCHITECTURE_IA64 +HANDLE_MDMP_ARCH(0x0007, Alpha64) // PROCESSOR_ARCHITECTURE_ALPHA64 +HANDLE_MDMP_ARCH(0x0008, MSIL) // PROCESSOR_ARCHITECTURE_MSIL +HANDLE_MDMP_ARCH(0x0009, AMD64) // PROCESSOR_ARCHITECTURE_AMD64 +HANDLE_MDMP_ARCH(0x000a, X86Win64) // PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 +HANDLE_MDMP_ARCH(0x8001, SPARC) // Breakpad-defined value for SPARC +HANDLE_MDMP_ARCH(0x8002, PPC64) // Breakpad-defined value for PPC64 +HANDLE_MDMP_ARCH(0x8003, ARM64) // Breakpad-defined value for ARM64 +HANDLE_MDMP_ARCH(0x8004, MIPS64) // Breakpad-defined value for MIPS64 + +HANDLE_MDMP_PLATFORM(0x0000, Win32S) // Win32 on Windows 3.1 +HANDLE_MDMP_PLATFORM(0x0001, Win32Windows) // Windows 95-98-Me +HANDLE_MDMP_PLATFORM(0x0002, Win32NT) // Windows NT, 2000+ +HANDLE_MDMP_PLATFORM(0x0003, Win32CE) // Windows CE, Windows Mobile, "Handheld" +// Breakpad-defined values. +HANDLE_MDMP_PLATFORM(0x8000, Unix) // Generic Unix-ish +HANDLE_MDMP_PLATFORM(0x8101, MacOSX) // Mac OS X/Darwin +HANDLE_MDMP_PLATFORM(0x8102, IOS) // iOS +HANDLE_MDMP_PLATFORM(0x8201, Linux) // Linux +HANDLE_MDMP_PLATFORM(0x8202, Solaris) // Solaris +HANDLE_MDMP_PLATFORM(0x8203, Android) // Android +HANDLE_MDMP_PLATFORM(0x8204, PS3) // PS3 +HANDLE_MDMP_PLATFORM(0x8205, NaCl) // Native Client (NaCl) + +#undef HANDLE_MDMP_STREAM_TYPE +#undef HANDLE_MDMP_ARCH +#undef HANDLE_MDMP_PLATFORM Index: llvm/trunk/include/llvm/Object/Binary.h =================================================================== --- llvm/trunk/include/llvm/Object/Binary.h +++ llvm/trunk/include/llvm/Object/Binary.h @@ -41,7 +41,9 @@ ID_Archive, ID_MachOUniversalBinary, ID_COFFImportFile, - ID_IR, // LLVM IR + ID_IR, // LLVM IR + + ID_Minidump, ID_WinRes, // Windows resource (.res) file. @@ -127,6 +129,8 @@ return TypeID == ID_IR; } + bool isMinidump() const { return TypeID == ID_Minidump; } + bool isLittleEndian() const { return !(TypeID == ID_ELF32B || TypeID == ID_ELF64B || TypeID == ID_MachO32B || TypeID == ID_MachO64B); Index: llvm/trunk/include/llvm/Object/Minidump.h =================================================================== --- llvm/trunk/include/llvm/Object/Minidump.h +++ llvm/trunk/include/llvm/Object/Minidump.h @@ -0,0 +1,121 @@ +//===- Minidump.h - Minidump object file implementation ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECT_MINIDUMP_H +#define LLVM_OBJECT_MINIDUMP_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/BinaryFormat/Minidump.h" +#include "llvm/Object/Binary.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace object { + +/// A class providing access to the contents of a minidump file. +class MinidumpFile : public Binary { +public: + /// Construct a new MinidumpFile object from the given memory buffer. Returns + /// an error if this file cannot be identified as a minidump file, or if its + /// contents are badly corrupted (i.e. we cannot read the stream directory). + static Expected> create(MemoryBufferRef Source); + + static bool classof(const Binary *B) { return B->isMinidump(); } + + /// Returns the contents of the minidump header. + const minidump::Header &header() const { return Header; } + + /// Returns the list of streams (stream directory entries) in this file. + ArrayRef streams() const { return Streams; } + + /// Returns the raw contents of the stream given by the directory entry. + ArrayRef getRawStream(const minidump::Directory &Stream) const { + return getData().slice(Stream.Location.RVA, Stream.Location.DataSize); + } + + /// Returns the raw contents of the stream of the given type, or None if the + /// file does not contain a stream of this type. + Optional> getRawStream(minidump::StreamType Type) 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 + /// internal consistency of the stream is not checked in any way. + Expected getSystemInfo() const { + return getStream(minidump::StreamType::SystemInfo); + } + +private: + static Error createError(StringRef Str, + object_error Err = object_error::parse_failed) { + return make_error(Str, Err); + } + + static Error createEOFError() { + return createError("Unexpected EOF", object_error::unexpected_eof); + } + + /// Return a slice of the given data array, with bounds checking. + static Expected> getDataSlice(ArrayRef Data, + size_t Offset, size_t Size); + + /// Return the slice of the given data array as an array of objects of the + /// given type. The function checks that the input array is large enough to + /// contain the correct number of objects of the given type. + template + static Expected> getDataSliceAs(ArrayRef Data, + size_t Offset, size_t Count); + + MinidumpFile(MemoryBufferRef Source, const minidump::Header &Header, + ArrayRef Streams, + DenseMap StreamMap) + : Binary(ID_Minidump, Source), Header(Header), Streams(Streams), + StreamMap(std::move(StreamMap)) {} + + ArrayRef getData() const { + return arrayRefFromStringRef(Data.getBuffer()); + } + + /// Return the stream of the given type, cast to the appropriate type. Checks + /// that the stream is large enough to hold an object of this type. + template + Expected getStream(minidump::StreamType Stream) const; + + const minidump::Header &Header; + ArrayRef Streams; + DenseMap StreamMap; +}; + +template +Expected MinidumpFile::getStream(minidump::StreamType Stream) const { + if (auto OptionalStream = getRawStream(Stream)) { + if (OptionalStream->size() >= sizeof(T)) + return *reinterpret_cast(OptionalStream->data()); + return createError("Malformed stream", object_error::unexpected_eof); + } + return createError("No such stream", object_error::invalid_section_index); +} + +template +Expected> MinidumpFile::getDataSliceAs(ArrayRef Data, + size_t Offset, + size_t Count) { + // Check for overflow. + if (Count > std::numeric_limits::max() / sizeof(T)) + return createEOFError(); + auto ExpectedArray = getDataSlice(Data, Offset, sizeof(T) * Count); + if (!ExpectedArray) + return ExpectedArray.takeError(); + return ArrayRef(reinterpret_cast(ExpectedArray->data()), Count); +} + +} // end namespace object +} // end namespace llvm + +#endif // LLVM_OBJECT_MINIDUMP_H Index: llvm/trunk/lib/BinaryFormat/CMakeLists.txt =================================================================== --- llvm/trunk/lib/BinaryFormat/CMakeLists.txt +++ llvm/trunk/lib/BinaryFormat/CMakeLists.txt @@ -2,6 +2,7 @@ AMDGPUMetadataVerifier.cpp Dwarf.cpp Magic.cpp + Minidump.cpp MsgPackDocument.cpp MsgPackDocumentYAML.cpp MsgPackReader.cpp Index: llvm/trunk/lib/BinaryFormat/Magic.cpp =================================================================== --- llvm/trunk/lib/BinaryFormat/Magic.cpp +++ llvm/trunk/lib/BinaryFormat/Magic.cpp @@ -181,7 +181,8 @@ return file_magic::coff_object; break; - case 'M': // Possible MS-DOS stub on Windows PE file or MSF/PDB file. + case 'M': // Possible MS-DOS stub on Windows PE file, MSF/PDB file or a + // Minidump file. if (startswith(Magic, "MZ") && Magic.size() >= 0x3c + 4) { uint32_t off = read32le(Magic.data() + 0x3c); // PE/COFF file, either EXE or DLL. @@ -191,6 +192,8 @@ } if (Magic.startswith("Microsoft C/C++ MSF 7.00\r\n")) return file_magic::pdb; + if (startswith(Magic, "MDMP")) + return file_magic::minidump; break; case 0x64: // x86-64 or ARM64 Windows. Index: llvm/trunk/lib/BinaryFormat/Minidump.cpp =================================================================== --- llvm/trunk/lib/BinaryFormat/Minidump.cpp +++ llvm/trunk/lib/BinaryFormat/Minidump.cpp @@ -0,0 +1,14 @@ +//===-- Minidump.cpp - Minidump constants and structures ---------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/Minidump.h" + +using namespace llvm::minidump; + +constexpr uint32_t Header::MagicSignature; +constexpr uint16_t Header::MagicVersion; Index: llvm/trunk/lib/Object/Binary.cpp =================================================================== --- llvm/trunk/lib/Object/Binary.cpp +++ llvm/trunk/lib/Object/Binary.cpp @@ -16,6 +16,7 @@ #include "llvm/Object/Archive.h" #include "llvm/Object/Error.h" #include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/Minidump.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/WindowsResource.h" #include "llvm/Support/Error.h" @@ -81,6 +82,8 @@ case file_magic::coff_cl_gl_object: // Unrecognized object file format. return errorCodeToError(object_error::invalid_file_type); + case file_magic::minidump: + return MinidumpFile::create(Buffer); } llvm_unreachable("Unexpected Binary File Type"); } Index: llvm/trunk/lib/Object/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Object/CMakeLists.txt +++ llvm/trunk/lib/Object/CMakeLists.txt @@ -13,6 +13,7 @@ IRSymtab.cpp MachOObjectFile.cpp MachOUniversal.cpp + Minidump.cpp ModuleSymbolTable.cpp Object.cpp ObjectFile.cpp Index: llvm/trunk/lib/Object/Minidump.cpp =================================================================== --- llvm/trunk/lib/Object/Minidump.cpp +++ llvm/trunk/lib/Object/Minidump.cpp @@ -0,0 +1,77 @@ +//===- Minidump.cpp - Minidump object file implementation -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/Minidump.h" +#include "llvm/Object/Error.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::minidump; + +Optional> +MinidumpFile::getRawStream(minidump::StreamType Type) const { + auto It = StreamMap.find(Type); + if (It != StreamMap.end()) + return getRawStream(Streams[It->second]); + return None; +} + +Expected> +MinidumpFile::getDataSlice(ArrayRef Data, size_t Offset, size_t Size) { + // Check for overflow. + if (Offset + Size < Offset || Offset + Size < Size || + Offset + Size > Data.size()) + return createEOFError(); + return Data.slice(Offset, Size); +} + +Expected> +MinidumpFile::create(MemoryBufferRef Source) { + ArrayRef Data = arrayRefFromStringRef(Source.getBuffer()); + auto ExpectedHeader = getDataSliceAs(Data, 0, 1); + if (!ExpectedHeader) + return ExpectedHeader.takeError(); + + const minidump::Header &Hdr = (*ExpectedHeader)[0]; + if (Hdr.Signature != Header::MagicSignature) + return createError("Invalid signature"); + if ((Hdr.Version & 0xffff) != Header::MagicVersion) + return createError("Invalid version"); + + auto ExpectedStreams = getDataSliceAs(Data, Hdr.StreamDirectoryRVA, + Hdr.NumberOfStreams); + if (!ExpectedStreams) + return ExpectedStreams.takeError(); + + DenseMap StreamMap; + for (const auto &Stream : llvm::enumerate(*ExpectedStreams)) { + StreamType Type = Stream.value().Type; + const LocationDescriptor &Loc = Stream.value().Location; + + auto ExpectedStream = getDataSlice(Data, Loc.RVA, Loc.DataSize); + if (!ExpectedStream) + return ExpectedStream.takeError(); + + if (Type == StreamType::Unused && Loc.DataSize == 0) { + // Ignore dummy streams. This is technically ill-formed, but a number of + // existing minidumps seem to contain such streams. + continue; + } + + if (Type == DenseMapInfo::getEmptyKey() || + Type == DenseMapInfo::getTombstoneKey()) + return createError("Cannot handle one of the minidump streams"); + + // Update the directory map, checking for duplicate stream types. + if (!StreamMap.try_emplace(Type, Stream.index()).second) + return createError("Duplicate stream type"); + } + + return std::unique_ptr( + new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap))); +} Index: llvm/trunk/lib/Object/ObjectFile.cpp =================================================================== --- llvm/trunk/lib/Object/ObjectFile.cpp +++ llvm/trunk/lib/Object/ObjectFile.cpp @@ -127,6 +127,7 @@ case file_magic::macho_universal_binary: case file_magic::windows_resource: case file_magic::pdb: + case file_magic::minidump: return errorCodeToError(object_error::invalid_file_type); case file_magic::elf: case file_magic::elf_relocatable: Index: llvm/trunk/lib/Object/SymbolicFile.cpp =================================================================== --- llvm/trunk/lib/Object/SymbolicFile.cpp +++ llvm/trunk/lib/Object/SymbolicFile.cpp @@ -52,6 +52,7 @@ case file_magic::macho_universal_binary: case file_magic::windows_resource: case file_magic::pdb: + case file_magic::minidump: return errorCodeToError(object_error::invalid_file_type); case file_magic::elf: case file_magic::elf_executable: Index: llvm/trunk/unittests/Object/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/Object/CMakeLists.txt +++ llvm/trunk/unittests/Object/CMakeLists.txt @@ -3,7 +3,9 @@ ) add_llvm_unittest(ObjectTests + MinidumpTest.cpp SymbolSizeTest.cpp SymbolicFileTest.cpp ) +target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport) Index: llvm/trunk/unittests/Object/MinidumpTest.cpp =================================================================== --- llvm/trunk/unittests/Object/MinidumpTest.cpp +++ llvm/trunk/unittests/Object/MinidumpTest.cpp @@ -0,0 +1,256 @@ +//===- MinidumpTest.cpp - Tests for Minidump.cpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/Minidump.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::object; +using namespace minidump; + +static Expected> create(ArrayRef Data) { + return MinidumpFile::create( + MemoryBufferRef(toStringRef(Data), "Test buffer")); +} + +TEST(MinidumpFile, BasicInterface) { + // A very simple minidump file which contains just a single stream. + auto ExpectedFile = + create({ // 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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + const Header &H = File.header(); + EXPECT_EQ(Header::MagicSignature, H.Signature); + EXPECT_EQ(Header::MagicVersion, H.Version); + EXPECT_EQ(1u, H.NumberOfStreams); + EXPECT_EQ(0x20u, H.StreamDirectoryRVA); + EXPECT_EQ(0x03020100u, H.Checksum); + EXPECT_EQ(0x07060504u, H.TimeDateStamp); + EXPECT_EQ(uint64_t(0x0504030201000908), H.Flags); + + ASSERT_EQ(1u, File.streams().size()); + const Directory &Stream0 = File.streams()[0]; + EXPECT_EQ(StreamType::LinuxCPUInfo, Stream0.Type); + EXPECT_EQ(7u, Stream0.Location.DataSize); + EXPECT_EQ(0x2cu, Stream0.Location.RVA); + + EXPECT_EQ("CPUINFO", toStringRef(File.getRawStream(Stream0))); + EXPECT_EQ("CPUINFO", + toStringRef(*File.getRawStream(StreamType::LinuxCPUInfo))); + + EXPECT_THAT_EXPECTED(File.getSystemInfo(), Failed()); +} + +// Use the input from the previous test, but corrupt it in various ways +TEST(MinidumpFile, create_ErrorCases) { + // File too short + EXPECT_THAT_EXPECTED(create({'M', 'D', 'M', 'P'}), Failed()); + + // Wrong Signature + EXPECT_THAT_EXPECTED( + create({ // Header + '!', '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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Wrong Version + EXPECT_THAT_EXPECTED( + create({ // Header + 'M', 'D', 'M', 'P', 0x39, 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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Stream directory after EOF + EXPECT_THAT_EXPECTED( + create({ // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 0, 0, 0, // NumberOfStreams, + 0x20, 1, 0, 0, // StreamDirectoryRVA + 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Truncated stream directory + EXPECT_THAT_EXPECTED( + create({ // Header + 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version + 1, 1, 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 + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Stream0 after EOF + EXPECT_THAT_EXPECTED( + create({ // 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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 1, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Truncated Stream0 + EXPECT_THAT_EXPECTED( + create({ // 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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 3, 0, 0x67, 0x47, 8, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Duplicate Stream + EXPECT_THAT_EXPECTED( + create({ // 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 + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x40, 0, 0, 0, // RVA + // Stream + 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, + 0x40, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); + + // Stream matching one of the DenseMapInfo magic values + EXPECT_THAT_EXPECTED( + create({ // 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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 0xff, 0xff, 0xff, 0xff, 7, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // Stream + 'C', 'P', 'U', 'I', 'N', 'F', 'O'}), + Failed()); +} + +TEST(MinidumpFile, IngoresDummyStreams) { + auto ExpectedFile = create({ + // 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 + 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, + 0x20, 0, 0, 0, // RVA + }); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + ASSERT_EQ(2u, File.streams().size()); + EXPECT_EQ(StreamType::Unused, File.streams()[0].Type); + EXPECT_EQ(StreamType::Unused, File.streams()[1].Type); + EXPECT_EQ(None, File.getRawStream(StreamType::Unused)); +} + +TEST(MinidumpFile, getSystemInfo) { + auto ExpectedFile = create({ + // 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 + 8, 9, 0, 1, 2, 3, 4, 5, // Flags + // Stream Directory + 7, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize, + 0x2c, 0, 0, 0, // RVA + // SystemInfo + 0, 0, 1, 2, // ProcessorArch, ProcessorLevel + 3, 4, 5, 6, // ProcessorRevision, NumberOfProcessors, ProductType + 7, 8, 9, 0, 1, 2, 3, 4, // MajorVersion, MinorVersion + 5, 6, 7, 8, 2, 0, 0, 0, // BuildNumber, PlatformId + 1, 2, 3, 4, 5, 6, 7, 8, // CSDVersionRVA, SuiteMask, Reserved + 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', // VendorID + 1, 2, 3, 4, 5, 6, 7, 8, // VersionInfo, FeatureInfo + 9, 0, 1, 2, // AMDExtendedFeatures + }); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + + auto ExpectedInfo = File.getSystemInfo(); + ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); + const SystemInfo &Info = *ExpectedInfo; + EXPECT_EQ(ProcessorArchitecture::X86, Info.ProcessorArch); + EXPECT_EQ(0x0201, Info.ProcessorLevel); + EXPECT_EQ(0x0403, Info.ProcessorRevision); + EXPECT_EQ(5, Info.NumberOfProcessors); + EXPECT_EQ(6, Info.ProductType); + EXPECT_EQ(0x00090807u, Info.MajorVersion); + EXPECT_EQ(0x04030201u, Info.MinorVersion); + EXPECT_EQ(0x08070605u, Info.BuildNumber); + EXPECT_EQ(OSPlatform::Win32NT, Info.PlatformId); + EXPECT_EQ(0x04030201u, Info.CSDVersionRVA); + EXPECT_EQ(0x0605u, Info.SuiteMask); + EXPECT_EQ(0x0807u, Info.Reserved); + EXPECT_EQ("LLVMLLVMLLVM", llvm::StringRef(Info.CPU.X86.VendorID, + sizeof(Info.CPU.X86.VendorID))); + EXPECT_EQ(0x04030201u, Info.CPU.X86.VersionInfo); + EXPECT_EQ(0x08070605u, Info.CPU.X86.FeatureInfo); + EXPECT_EQ(0x02010009u, Info.CPU.X86.AMDExtendedFeatures); +}