1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +01:00

[Object] Add basic minidump support

Summary:
This patch adds basic support for reading minidump files. It contains
the definitions of various important minidump data structures (header,
stream directory), and of one minidump stream (SystemInfo). The ability
to read other streams will be added in follow-up patches. However, all
streams can be read even now as raw data, which means lldb's minidump
support (where this code is taken from) can be immediately rebased on
top of this patch as soon as it lands.

As we don't have any support for generating minidump files (yet), this
tests the code via unit tests with some small handcrafted binaries in
the form of c char arrays.

Reviewers: Bigcheese, jhenderson, zturner

Subscribers: srhines, dschuff, mgorny, fedor.sergeev, lemo, clayborg, JDevlieghere, aprantl, lldb-commits, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D59291

llvm-svn: 356652
This commit is contained in:
Pavel Labath 2019-03-21 09:18:59 +00:00
parent 5bc40f99c0
commit 6587b1348f
15 changed files with 741 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -43,6 +43,8 @@ protected:
ID_COFFImportFile,
ID_IR, // LLVM IR
ID_Minidump,
ID_WinRes, // Windows resource (.res) file.
// Object and children.
@ -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);

View File

@ -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

View File

@ -2,6 +2,7 @@ add_llvm_library(LLVMBinaryFormat
AMDGPUMetadataVerifier.cpp
Dwarf.cpp
Magic.cpp
Minidump.cpp
MsgPackDocument.cpp
MsgPackDocumentYAML.cpp
MsgPackReader.cpp

View File

@ -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.

View File

@ -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;

View File

@ -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");
}

View File

@ -13,6 +13,7 @@ add_llvm_library(LLVMObject
IRSymtab.cpp
MachOObjectFile.cpp
MachOUniversal.cpp
Minidump.cpp
ModuleSymbolTable.cpp
Object.cpp
ObjectFile.cpp

77
lib/Object/Minidump.cpp Normal file
View File

@ -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)));
}

View File

@ -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:

View File

@ -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:

View File

@ -3,7 +3,9 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_unittest(ObjectTests
MinidumpTest.cpp
SymbolSizeTest.cpp
SymbolicFileTest.cpp
)
target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport)

View File

@ -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);
}