Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -54,6 +54,7 @@ bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; + bool BuildId; bool Demangle = true; bool DiscardAll; bool DiscardLocals; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -223,6 +223,7 @@ Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->BuildId = Args.hasArg(OPT_build_id); Config->Demangle = !Args.hasArg(OPT_no_demangle); Config->DiscardAll = Args.hasArg(OPT_discard_all); Config->DiscardLocals = Args.hasArg(OPT_discard_locals); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -12,6 +12,9 @@ def Bstatic: Flag<["-"], "Bstatic">, HelpText<"Do not link against shared libraries">; +def build_id : Flag<["--", "-"], "build-id">, + HelpText<"Generate build ID note">; + def L : JoinedOrSeparate<["-"], "L">, MetaVarName<"">, HelpText<"Directory to search for libraries">; @@ -172,7 +175,6 @@ def start_group_paren: Flag<["-"], "(">; // Options listed below are silently ignored for now for compatibility. -def build_id : Flag<["--"], "build-id">; def fatal_warnings : Flag<["--"], "fatal-warnings">; def no_add_needed : Flag<["--"], "no-add-needed">; def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -511,12 +511,26 @@ std::vector FdeList; }; +template +class BuildIdSection final : public OutputSectionBase { +public: + BuildIdSection(); + void writeTo(uint8_t *Buf) override; + void update(ArrayRef Buf); + void writeBuildId(); + +private: + uint64_t Hash = 0xcbf29ce484222325; // FNV1 hash basis + uint8_t *HashBuf; +}; + // All output sections that are hadnled by the linker specially are // globally accessible. Writer initializes them, so don't use them // until Writer is initialized. template struct Out { typedef typename llvm::object::ELFFile::uintX_t uintX_t; typedef typename llvm::object::ELFFile::Elf_Phdr Elf_Phdr; + static BuildIdSection *BuildId; static DynamicSection *Dynamic; static EhFrameHeader *EhFrameHdr; static GnuHashTableSection *GnuHashTab; @@ -541,6 +555,7 @@ static OutputSectionBase *ProgramHeaders; }; +template BuildIdSection *Out::BuildId; template DynamicSection *Out::Dynamic; template EhFrameHeader *Out::EhFrameHdr; template GnuHashTableSection *Out::GnuHashTab; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -1530,6 +1530,36 @@ } template +BuildIdSection::BuildIdSection() + : OutputSectionBase(".note.gnu.build-id", SHT_NOTE, SHF_ALLOC) { + // 16 bytes for the note section header and 8 bytes for FNV1 hash. + this->Header.sh_size = 24; +} + +template void BuildIdSection::writeTo(uint8_t *Buf) { + const endianness E = ELFT::TargetEndianness; + write32(Buf, 4); // Name size + write32(Buf + 4, sizeof(Hash)); // Content size + write32(Buf + 8, NT_GNU_BUILD_ID); // Type + memcpy(Buf + 12, "GNU", 4); // Name string + HashBuf = Buf + 16; +} + +template void BuildIdSection::update(ArrayRef Buf) { + // 64-bit FNV1 hash + const uint64_t Prime = 0x100000001b3; + for (uint8_t B : Buf) { + Hash *= Prime; + Hash ^= B; + } +} + +template void BuildIdSection::writeBuildId() { + const endianness E = ELFT::TargetEndianness; + write64(HashBuf, Hash); +} + +template MipsReginfoOutputSection::MipsReginfoOutputSection() : OutputSectionBase(".reginfo", SHT_MIPS_REGINFO, SHF_ALLOC) { this->Header.sh_addralign = 4; @@ -1632,5 +1662,10 @@ template class SymbolTableSection; template class SymbolTableSection; template class SymbolTableSection; + +template class BuildIdSection; +template class BuildIdSection; +template class BuildIdSection; +template class BuildIdSection; } } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -75,6 +75,7 @@ bool openFile(); void writeHeader(); void writeSections(); + void writeBuildId(); bool isDiscarded(InputSectionBase *IS) const; StringRef getOutputSectionName(InputSectionBase *S) const; bool needsInterpSection() const { @@ -143,6 +144,7 @@ ProgramHeaders.updateAlign(sizeof(uintX_t)); // Instantiate optional output sections if they are needed. + std::unique_ptr> BuildId; std::unique_ptr> GnuHashTab; std::unique_ptr> GotPlt; std::unique_ptr> HashTab; @@ -151,6 +153,8 @@ std::unique_ptr> SymTabSec; std::unique_ptr> MipsRldMap; + if (Config->BuildId) + BuildId.reset(new BuildIdSection); if (Config->GnuHash) GnuHashTab.reset(new GnuHashTableSection); if (Config->SysvHash) @@ -175,6 +179,7 @@ MipsRldMap->updateAlign(sizeof(uintX_t)); } + Out::BuildId = BuildId.get(); Out::DynStrTab = &DynStrTab; Out::DynSymTab = &DynSymTab; Out::Dynamic = &Dynamic; @@ -219,6 +224,7 @@ return; writeHeader(); writeSections(); + writeBuildId(); if (HasError) return; check(Buffer->commit()); @@ -1113,6 +1119,7 @@ // This order is not the same as the final output order // because we sort the sections using their attributes below. + Add(Out::BuildId); Add(Out::SymTab); Add(Out::ShStrTab); Add(Out::StrTab); @@ -1531,6 +1538,29 @@ Sec->writeTo(Buf + Sec->getFileOff()); } +template void Writer::writeBuildId() { + BuildIdSection *S = Out::BuildId; + if (!S) + return; + + // Compute a hash of all sections except .debug_* sections. + // We skip debug sections because they tend to be very large + // and their contents are very likely to be the same as long as + // other sections are the same. + uint8_t *Start = Buffer->getBufferStart(); + uint8_t *Last = Start; + for (OutputSectionBase *Sec : OutputSections) { + uint8_t *End = Start + Sec->getFileOff(); + if (!Sec->getName().startswith(".debug_")) + S->update({Last, End}); + Last = End; + } + S->update({Last, Start + FileSize}); + + // Fill the hash value field in the .note.gnu.build-id section. + S->writeBuildId(); +} + template void elf::writeResult(SymbolTable *Symtab); template void elf::writeResult(SymbolTable *Symtab); template void elf::writeResult(SymbolTable *Symtab); Index: test/ELF/build-id.s =================================================================== --- /dev/null +++ test/ELF/build-id.s @@ -0,0 +1,16 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld --build-id %t -o %t2 +# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=BUILDID %s +# RUN: ld.lld %t -o %t2 +# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=NO-BUILDID %s + +.globl _start; +_start: + nop + +# BUILDID: Contents of section .note.gnu.build-id: +# BUILDID-NEXT: 04000000 08000000 03000000 474e5500 ............GNU. + +# NO-BUILDID-NOT: Contents of section .note.gnu.build-id: