diff --git a/llvm/tools/llvm-objcopy/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt --- a/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/llvm/tools/llvm-objcopy/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + DebugInfoCodeView Object Option Support diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -14,8 +14,10 @@ #include "Writer.h" #include "llvm-objcopy.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" @@ -27,6 +29,7 @@ using namespace object; using namespace COFF; +using namespace codeview; static bool isDebugSection(const Section &Sec) { return Sec.Name.startswith(".debug"); @@ -87,6 +90,91 @@ Obj.addSections(Sections); } +static ArrayRef consumeDebugMagic(ArrayRef Data, + StringRef SecName) { + // First 4 bytes are section magic. + if (Data.size() < 4) + error(SecName + " too short"); + if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) + error(SecName + " has an invalid magic"); + return Data.slice(4); +} + +const std::vector mergeDebugT(const Section *Sec) { + std::vector OwnedHashes; + + ArrayRef Data = Sec->getContents(); + Data = consumeDebugMagic(Data, ".debug$T"); + if (Data.empty()) + return OwnedHashes; + + BinaryByteStream Stream(Data, support::little); + CVTypeArray Types; + BinaryStreamReader Reader(Stream); + if (auto EC = Reader.readArray(Types, Reader.getLength())) + error("Reader::readArray failed: " + toString(std::move(EC))); + + OwnedHashes = GloballyHashedType::hashTypes(Types); + return OwnedHashes; +} + +ArrayRef toDebugH(const std::vector hashes, + BumpPtrAllocator &Alloc) { + uint32_t Size = 8 + 8 * hashes.size(); + uint8_t *Data = Alloc.Allocate(Size); + MutableArrayRef Buffer(Data, Size); + BinaryStreamWriter Writer(Buffer, llvm::support::little); + uint32_t magic = COFF::DEBUG_HASHES_SECTION_MAGIC; + cantFail(Writer.writeInteger(magic)); + cantFail(Writer.writeInteger(uint16_t(0))); + cantFail(Writer.writeInteger(uint16_t(GlobalTypeHashAlg::SHA1_8))); + for (const auto &H : hashes) { + cantFail(Writer.writeBytes(H.Hash)); + } + assert(Writer.bytesRemaining() == 0); + return Buffer; +} + +static void addGHashes(Object &Obj) { + std::vector hashes; + + bool found_types = false; + + for (Section section: Obj.getSections()) { + if (section.Name == ".debug$H") + error("Already has .debug$H"); + + if (section.Name == ".debug$T") { + hashes = mergeDebugT(§ion); + found_types = true; + break; + } + } + + if (!found_types) + return; + + std::vector
Sections; + Section Sec; + BumpPtrAllocator Allocator; + Sec.setOwnedContents(toDebugH(hashes, Allocator)); + Sec.Name = ".debug$H"; + Sec.Header.VirtualSize = 0; + Sec.Header.VirtualAddress = 0; + Sec.Header.SizeOfRawData = Sec.getContents().size(); + // Sec.Header.PointerToRawData is filled in by the writer. + Sec.Header.PointerToRelocations = 0; + Sec.Header.PointerToLinenumbers = 0; + // Sec.Header.NumberOfRelocations is filled in by the writer. + Sec.Header.NumberOfLinenumbers = 0; + Sec.Header.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_ALIGN_4BYTES; + std::vector Symbols; + + Sections.push_back(Sec); + Obj.addSections(Sections); +} + static Error handleArgs(const CopyConfig &Config, Object &Obj) { // Perform the actual section removals. Obj.removeSections([&Config](const Section &Sec) { @@ -174,6 +262,9 @@ if (!Config.AddGnuDebugLink.empty()) addGnuDebugLink(Obj, Config.AddGnuDebugLink); + if (Config.AddGHashes) + addGHashes(Obj); + if (!Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || !Config.AddSection.empty() || diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -120,6 +120,7 @@ std::function EntryExpr; // Boolean options + bool AddGHashes = false; bool DeterministicArchives = true; bool ExtractDWO = false; bool KeepFileSymbols = false; diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -484,6 +484,7 @@ } Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); + Config.AddGHashes = InputArgs.hasArg(OBJCOPY_add_ghashes); Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) Config.BuildIdLinkInput = diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -63,6 +63,9 @@ : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for ">, MetaVarName<"debug-file">; +def add_ghashes : Flag<["-", "--"], "add-ghashes">, + HelpText<"Add .h global hashes">; + defm remove_section : Eq<"remove-section", "Remove
">, MetaVarName<"section">; def R : JoinedOrSeparate<["-"], "R">, Alias;