Index: lld/COFF/Config.h =================================================================== --- lld/COFF/Config.h +++ lld/COFF/Config.h @@ -10,8 +10,10 @@ #ifndef LLD_COFF_CONFIG_H #define LLD_COFF_CONFIG_H +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/GUID.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" #include @@ -99,6 +101,8 @@ bool DebugGHashes = false; bool ShowTiming = false; unsigned DebugTypes = static_cast(DebugType::None); + llvm::Optional PDBAge; + llvm::Optional PDBGuid; llvm::SmallString<128> PDBPath; std::vector Argv; Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -922,9 +922,23 @@ // Handle /pdb bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash); - if (ShouldCreatePDB) + if (ShouldCreatePDB) { if (auto *Arg = Args.getLastArg(OPT_pdb)) Config->PDBPath = Arg->getValue(); + if (auto *Arg = Args.getLastArg(OPT_pdbguid)) { + Expected ExpectedGuid = + codeview::guidFromText(Arg->getValue()); + if (!ExpectedGuid) { + consumeError(ExpectedGuid.takeError()); + error(Twine("/pdbguid: ") + Arg->getValue() + " is not a valid guid"); + } + Config->PDBGuid = *ExpectedGuid; + } + if (auto *Arg = Args.getLastArg(OPT_pdbage)) { + Config->PDBAge.emplace(); + StringRef(Arg->getValue()).getAsInteger(10, *Config->PDBAge); + } + } // Handle /noentry if (Args.hasArg(OPT_noentry)) { Index: lld/COFF/Options.td =================================================================== --- lld/COFF/Options.td +++ lld/COFF/Options.td @@ -46,6 +46,8 @@ def order : P<"order", "Put functions in order">; def out : P<"out", "Path to file to write output">; def pdb : P<"pdb", "PDB file path">; +def pdbage : P<"pdbage", "Specify age of PDB file">; +def pdbguid : P<"pdbguid", "GUID used to identify PDB file">; def section : P<"section", "Specify section attributes">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; Index: lld/COFF/Writer.cpp =================================================================== --- lld/COFF/Writer.cpp +++ lld/COFF/Writer.cpp @@ -1033,15 +1033,28 @@ assert(BuildId && "BuildId is not set!"); + // Use the previous build id from the existing binary, but override the values + // of guid and age if requested from the command line. if (PreviousBuildId.hasValue()) { *BuildId->BuildId = *PreviousBuildId; - BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1; - return; + BuildId->BuildId->PDB70.Age += 1; + } else { + BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70; + BuildId->BuildId->PDB70.Age = 1; + } + + if (Config->PDBGuid.hasValue()) { + static_assert(sizeof(BuildId->BuildId->PDB70.Signature) == + sizeof(Config->PDBGuid->Guid), + "Guid size mismatch."); + ::memcpy(BuildId->BuildId->PDB70.Signature, Config->PDBGuid->Guid, + sizeof(Config->PDBGuid->Guid)); + } else if (!PreviousBuildId.hasValue()) { + llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16); } - BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70; - BuildId->BuildId->PDB70.Age = 1; - llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16); + if (Config->PDBAge.hasValue()) + BuildId->BuildId->PDB70.Age = Config->PDBAge.getValue(); } // Sort .pdata section contents according to PE/COFF spec 5.5. Index: llvm/include/llvm/DebugInfo/CodeView/GUID.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/GUID.h +++ llvm/include/llvm/DebugInfo/CodeView/GUID.h @@ -10,6 +10,11 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_GUID_H #define LLVM_DEBUGINFO_CODEVIEW_GUID_H +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" + #include #include @@ -49,6 +54,13 @@ raw_ostream &operator<<(raw_ostream &OS, const GUID &Guid); +Expected guidFromText(StringRef S); +std::string textFromGuid(const GUID &Guid); +std::string textFromGuid(StringRef Guid); +std::string textFromGuid(ArrayRef Guid); +void textFromGuid(StringRef Guid, llvm::raw_ostream &OS); +void textFromGuid(ArrayRef Guid, llvm::raw_ostream &OS); + } // namespace codeview } // namespace llvm Index: llvm/lib/DebugInfo/CodeView/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/CodeView/CMakeLists.txt +++ llvm/lib/DebugInfo/CodeView/CMakeLists.txt @@ -20,6 +20,7 @@ EnumTables.cpp Formatters.cpp GlobalTypeTableBuilder.cpp + GUID.cpp LazyRandomTypeCollection.cpp Line.cpp MergingTypeTableBuilder.cpp Index: llvm/lib/DebugInfo/CodeView/Formatters.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/Formatters.cpp +++ llvm/lib/DebugInfo/CodeView/Formatters.cpp @@ -9,6 +9,7 @@ #include "llvm/DebugInfo/CodeView/Formatters.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/DebugInfo/CodeView/GUID.h" #include "llvm/Support/raw_ostream.h" #include @@ -25,20 +26,7 @@ : FormatAdapter(std::move(Guid)) {} void GuidAdapter::format(raw_ostream &Stream, StringRef Style) { - static const char *Lookup = "0123456789ABCDEF"; - - assert(Item.size() == 16 && "Expected 16-byte GUID"); - Stream << "{"; - for (int i = 0; i < 16;) { - uint8_t Byte = Item[i]; - uint8_t HighNibble = (Byte >> 4) & 0xF; - uint8_t LowNibble = Byte & 0xF; - Stream << Lookup[HighNibble] << Lookup[LowNibble]; - ++i; - if (i >= 4 && i <= 10 && i % 2 == 0) - Stream << "-"; - } - Stream << "}"; + textFromGuid(toStringRef(Item), Stream); } raw_ostream &llvm::codeview::operator<<(raw_ostream &OS, const GUID &Guid) { Index: llvm/lib/DebugInfo/CodeView/GUID.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/CodeView/GUID.cpp @@ -0,0 +1,92 @@ +//===- GUID.cpp -----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/GUID.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace llvm::codeview; + +Expected llvm::codeview::guidFromText(StringRef GuidStr) { + // We recognize the following two forms of Guid specification. + // {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} + // XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + // where X is a hex digit (in upper or lowercase). + if (GuidStr.size() != 36 && GuidStr.size() != 38) + return make_error("Invalid Guid string!", + inconvertibleErrorCode()); + + if (GuidStr.size() == 38) { + // Strip { and } from the beginning and end. + if (GuidStr.front() != '{' || GuidStr.back() != '}') + return make_error("Invalid Guid string!", + inconvertibleErrorCode()); + GuidStr = GuidStr.drop_front().drop_back(); + } + + assert(GuidStr.size() == 36); + SmallVector Groups; + GuidStr.split(Groups, '-'); + if (Groups.size() != 5 || Groups[0].size() != 8 || Groups[1].size() != 4 || + Groups[2].size() != 4 || Groups[3].size() != 4 || Groups[4].size() != 12) + return make_error("Invalid Guid string!", + inconvertibleErrorCode()); + GUID Result; + uint8_t *WritePtr = Result.Guid; + for (StringRef G : Groups) { + while (!G.empty()) { + if (!isHexDigit(G[0]) || !isHexDigit(G[1])) + return make_error("Invalid Guid string!", + inconvertibleErrorCode()); + + *WritePtr = hexFromNibbles(G[0], G[1]); + G = G.drop_front(2); + ++WritePtr; + } + } + return Result; +} + +std::string llvm::codeview::textFromGuid(const GUID &Guid) { + return textFromGuid(makeArrayRef(Guid.Guid)); +} + +std::string llvm::codeview::textFromGuid(StringRef Guid) { + return textFromGuid(arrayRefFromStringRef(Guid)); +} + +std::string llvm::codeview::textFromGuid(ArrayRef Guid) { + std::string Result; + llvm::raw_string_ostream OS(Result); + textFromGuid(Guid, OS); + OS.flush(); + return Result; +} + +void llvm::codeview::textFromGuid(ArrayRef Guid, + llvm::raw_ostream &OS) { + static const char *Lookup = "0123456789ABCDEF"; + + assert(Guid.size() == 16 && "Expected 16-byte GUID"); + OS << "{"; + for (int i = 0; i < 16;) { + uint8_t Byte = Guid[i]; + uint8_t HighNibble = (Byte >> 4) & 0xF; + uint8_t LowNibble = Byte & 0xF; + OS << Lookup[HighNibble] << Lookup[LowNibble]; + ++i; + if (i >= 4 && i <= 10 && i % 2 == 0) + OS << "-"; + } + OS << "}"; +} + +void llvm::codeview::textFromGuid(StringRef Guid, llvm::raw_ostream &OS) { + textFromGuid(arrayRefFromStringRef(Guid), OS); +}