summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/BinaryFormat/Magic.h1
-rw-r--r--llvm/include/llvm/BinaryFormat/Minidump.h147
-rw-r--r--llvm/include/llvm/BinaryFormat/MinidumpConstants.def107
-rw-r--r--llvm/include/llvm/Object/Binary.h6
-rw-r--r--llvm/include/llvm/Object/Minidump.h121
-rw-r--r--llvm/lib/BinaryFormat/CMakeLists.txt1
-rw-r--r--llvm/lib/BinaryFormat/Magic.cpp5
-rw-r--r--llvm/lib/BinaryFormat/Minidump.cpp14
-rw-r--r--llvm/lib/Object/Binary.cpp3
-rw-r--r--llvm/lib/Object/CMakeLists.txt1
-rw-r--r--llvm/lib/Object/Minidump.cpp77
-rw-r--r--llvm/lib/Object/ObjectFile.cpp1
-rw-r--r--llvm/lib/Object/SymbolicFile.cpp1
-rw-r--r--llvm/unittests/Object/CMakeLists.txt2
-rw-r--r--llvm/unittests/Object/MinidumpTest.cpp256
15 files changed, 741 insertions, 2 deletions
diff --git a/llvm/include/llvm/BinaryFormat/Magic.h b/llvm/include/llvm/BinaryFormat/Magic.h
index 05af2c367c8..091f63201e4 100644
--- a/llvm/include/llvm/BinaryFormat/Magic.h
+++ b/llvm/include/llvm/BinaryFormat/Magic.h
@@ -39,6 +39,7 @@ struct file_magic {
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
diff --git a/llvm/include/llvm/BinaryFormat/Minidump.h b/llvm/include/llvm/BinaryFormat/Minidump.h
new file mode 100644
index 00000000000..1e2dd972830
--- /dev/null
+++ b/llvm/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<StreamType> 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<ProcessorArchitecture> 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<OSPlatform> 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<minidump::StreamType> {
+ static minidump::StreamType getEmptyKey() { return minidump::StreamType(-1); }
+
+ static minidump::StreamType getTombstoneKey() {
+ return minidump::StreamType(-2);
+ }
+
+ static unsigned getHashValue(minidump::StreamType Val) {
+ return DenseMapInfo<uint32_t>::getHashValue(static_cast<uint32_t>(Val));
+ }
+
+ static bool isEqual(minidump::StreamType LHS, minidump::StreamType RHS) {
+ return LHS == RHS;
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_BINARYFORMAT_MINIDUMP_H
diff --git a/llvm/include/llvm/BinaryFormat/MinidumpConstants.def b/llvm/include/llvm/BinaryFormat/MinidumpConstants.def
new file mode 100644
index 00000000000..d4f13dd9921
--- /dev/null
+++ b/llvm/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
diff --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h
index 8ce2d5aa402..4d5ca8cc96a 100644
--- a/llvm/include/llvm/Object/Binary.h
+++ b/llvm/include/llvm/Object/Binary.h
@@ -41,7 +41,9 @@ protected:
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 @@ public:
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);
diff --git a/llvm/include/llvm/Object/Minidump.h b/llvm/include/llvm/Object/Minidump.h
new file mode 100644
index 00000000000..bb7d95bd546
--- /dev/null
+++ b/llvm/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<std::unique_ptr<MinidumpFile>> 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<minidump::Directory> streams() const { return Streams; }
+
+ /// Returns the raw contents of the stream given by the directory entry.
+ ArrayRef<uint8_t> 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<ArrayRef<uint8_t>> 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<const minidump::SystemInfo &> getSystemInfo() const {
+ return getStream<minidump::SystemInfo>(minidump::StreamType::SystemInfo);
+ }
+
+private:
+ static Error createError(StringRef Str,
+ object_error Err = object_error::parse_failed) {
+ return make_error<GenericBinaryError>(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<ArrayRef<uint8_t>> getDataSlice(ArrayRef<uint8_t> 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 <typename T>
+ static Expected<ArrayRef<T>> getDataSliceAs(ArrayRef<uint8_t> Data,
+ size_t Offset, size_t Count);
+
+ MinidumpFile(MemoryBufferRef Source, const minidump::Header &Header,
+ ArrayRef<minidump::Directory> Streams,
+ DenseMap<minidump::StreamType, std::size_t> StreamMap)
+ : Binary(ID_Minidump, Source), Header(Header), Streams(Streams),
+ StreamMap(std::move(StreamMap)) {}
+
+ ArrayRef<uint8_t> 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 <typename T>
+ Expected<const T &> getStream(minidump::StreamType Stream) const;
+
+ const minidump::Header &Header;
+ ArrayRef<minidump::Directory> Streams;
+ DenseMap<minidump::StreamType, std::size_t> StreamMap;
+};
+
+template <typename T>
+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 createError("No such stream", object_error::invalid_section_index);
+}
+
+template <typename T>
+Expected<ArrayRef<T>> MinidumpFile::getDataSliceAs(ArrayRef<uint8_t> Data,
+ size_t Offset,
+ size_t Count) {
+ // Check for overflow.
+ if (Count > std::numeric_limits<size_t>::max() / sizeof(T))
+ return createEOFError();
+ auto ExpectedArray = getDataSlice(Data, Offset, sizeof(T) * Count);
+ if (!ExpectedArray)
+ return ExpectedArray.takeError();
+ return ArrayRef<T>(reinterpret_cast<const T *>(ExpectedArray->data()), Count);
+}
+
+} // end namespace object
+} // end namespace llvm
+
+#endif // LLVM_OBJECT_MINIDUMP_H
diff --git a/llvm/lib/BinaryFormat/CMakeLists.txt b/llvm/lib/BinaryFormat/CMakeLists.txt
index 98ed8472af7..5d792a6abca 100644
--- a/llvm/lib/BinaryFormat/CMakeLists.txt
+++ b/llvm/lib/BinaryFormat/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_library(LLVMBinaryFormat
AMDGPUMetadataVerifier.cpp
Dwarf.cpp
Magic.cpp
+ Minidump.cpp
MsgPackDocument.cpp
MsgPackDocumentYAML.cpp
MsgPackReader.cpp
diff --git a/llvm/lib/BinaryFormat/Magic.cpp b/llvm/lib/BinaryFormat/Magic.cpp
index 5e9d6e73257..85f944fa6ab 100644
--- a/llvm/lib/BinaryFormat/Magic.cpp
+++ b/llvm/lib/BinaryFormat/Magic.cpp
@@ -181,7 +181,8 @@ file_magic llvm::identify_magic(StringRef Magic) {
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 @@ file_magic llvm::identify_magic(StringRef Magic) {
}
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.
diff --git a/llvm/lib/BinaryFormat/Minidump.cpp b/llvm/lib/BinaryFormat/Minidump.cpp
new file mode 100644
index 00000000000..b618fb15701
--- /dev/null
+++ b/llvm/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;
diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp
index 87b2c1278c0..e1063a64b14 100644
--- a/llvm/lib/Object/Binary.cpp
+++ b/llvm/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 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
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");
}
diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt
index fd5e7707c54..a0ac0046c0b 100644
--- a/llvm/lib/Object/CMakeLists.txt
+++ b/llvm/lib/Object/CMakeLists.txt
@@ -13,6 +13,7 @@ add_llvm_library(LLVMObject
IRSymtab.cpp
MachOObjectFile.cpp
MachOUniversal.cpp
+ Minidump.cpp
ModuleSymbolTable.cpp
Object.cpp
ObjectFile.cpp
diff --git a/llvm/lib/Object/Minidump.cpp b/llvm/lib/Object/Minidump.cpp
new file mode 100644
index 00000000000..d0d49b4e8d3
--- /dev/null
+++ b/llvm/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<ArrayRef<uint8_t>>
+MinidumpFile::getRawStream(minidump::StreamType Type) const {
+ auto It = StreamMap.find(Type);
+ if (It != StreamMap.end())
+ return getRawStream(Streams[It->second]);
+ return None;
+}
+
+Expected<ArrayRef<uint8_t>>
+MinidumpFile::getDataSlice(ArrayRef<uint8_t> 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<std::unique_ptr<MinidumpFile>>
+MinidumpFile::create(MemoryBufferRef Source) {
+ ArrayRef<uint8_t> Data = arrayRefFromStringRef(Source.getBuffer());
+ auto ExpectedHeader = getDataSliceAs<minidump::Header>(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<Directory>(Data, Hdr.StreamDirectoryRVA,
+ Hdr.NumberOfStreams);
+ if (!ExpectedStreams)
+ return ExpectedStreams.takeError();
+
+ DenseMap<StreamType, std::size_t> 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<StreamType>::getEmptyKey() ||
+ Type == DenseMapInfo<StreamType>::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<MinidumpFile>(
+ new MinidumpFile(Source, Hdr, *ExpectedStreams, std::move(StreamMap)));
+}
diff --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp
index 441ef54bd84..cf095db8e8c 100644
--- a/llvm/lib/Object/ObjectFile.cpp
+++ b/llvm/lib/Object/ObjectFile.cpp
@@ -127,6 +127,7 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type) {
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:
diff --git a/llvm/lib/Object/SymbolicFile.cpp b/llvm/lib/Object/SymbolicFile.cpp
index 8ee82567710..675f2091518 100644
--- a/llvm/lib/Object/SymbolicFile.cpp
+++ b/llvm/lib/Object/SymbolicFile.cpp
@@ -52,6 +52,7 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type,
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:
diff --git a/llvm/unittests/Object/CMakeLists.txt b/llvm/unittests/Object/CMakeLists.txt
index e1376bffbc0..e0fc6ce8893 100644
--- a/llvm/unittests/Object/CMakeLists.txt
+++ b/llvm/unittests/Object/CMakeLists.txt
@@ -3,7 +3,9 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_unittest(ObjectTests
+ MinidumpTest.cpp
SymbolSizeTest.cpp
SymbolicFileTest.cpp
)
+target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport)
diff --git a/llvm/unittests/Object/MinidumpTest.cpp b/llvm/unittests/Object/MinidumpTest.cpp
new file mode 100644
index 00000000000..6ebb2a98952
--- /dev/null
+++ b/llvm/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<std::unique_ptr<MinidumpFile>> create(ArrayRef<uint8_t> 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<BinaryError>());
+}
+
+// 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+
+ // 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<BinaryError>());
+}
+
+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);
+}
OpenPOWER on IntegriCloud