forked from OSchip/llvm-project
ELF: Implement --build-id.
This patch implements --build-id. After the linker creates an output file in the memory buffer, it computes the FNV1 hash of the resulting file and set the hash to the .note section as a build-id. GNU ld and gold have the same feature, but their default choice of the hash function is different. Their default is SHA1. We made a deliberate choice to not use a secure hash function for the sake of performance. Computing a secure hash is slow -- for example, MD5 throughput is usually 400 MB/s or so. SHA1 is slower than that. As a result, if you pass --build-id to gold, then the linker becomes about 10% slower than that without the option. We observed a similar degradation in an experimental implementation of build-id for LLD. On the other hand, we observed only 1-2% performance degradation with the FNV hash. Since build-id is not for digital certificate or anything, we think that a very small probability of collision is acceptable. We considered using other signals such as using input file timestamps as inputs to a secure hash function. But such signals would have an issue with build reproducibility (if you build a binary from the same source tree using the same toolchain, the build id should become the same.) GNU linkers accepts --build-id=<style> option where style is one of "MD5", "SHA1", or an arbitrary hex string. That option is out of scope of this patch. http://reviews.llvm.org/D18091 llvm-svn: 263292
This commit is contained in:
parent
495e153ff9
commit
634ddf0bec
|
@ -54,6 +54,7 @@ struct Configuration {
|
|||
bool AsNeeded = false;
|
||||
bool Bsymbolic;
|
||||
bool BsymbolicFunctions;
|
||||
bool BuildId;
|
||||
bool Demangle = true;
|
||||
bool DiscardAll;
|
||||
bool DiscardLocals;
|
||||
|
|
|
@ -223,6 +223,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
|||
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);
|
||||
|
|
|
@ -12,6 +12,9 @@ def Bdynamic: Flag<["-"], "Bdynamic">,
|
|||
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<"<dir>">,
|
||||
HelpText<"Directory to search for libraries">;
|
||||
|
||||
|
@ -172,7 +175,6 @@ def start_group : Flag<["--"], "start-group">;
|
|||
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">;
|
||||
|
|
|
@ -1529,6 +1529,36 @@ uint8_t SymbolTableSection<ELFT>::getSymbolBinding(SymbolBody *Body) {
|
|||
return Body->isWeak() ? STB_WEAK : STB_GLOBAL;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
BuildIdSection<ELFT>::BuildIdSection()
|
||||
: OutputSectionBase<ELFT>(".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 <class ELFT> void BuildIdSection<ELFT>::writeTo(uint8_t *Buf) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
write32<E>(Buf, 4); // Name size
|
||||
write32<E>(Buf + 4, sizeof(Hash)); // Content size
|
||||
write32<E>(Buf + 8, NT_GNU_BUILD_ID); // Type
|
||||
memcpy(Buf + 12, "GNU", 4); // Name string
|
||||
HashBuf = Buf + 16;
|
||||
}
|
||||
|
||||
template <class ELFT> void BuildIdSection<ELFT>::update(ArrayRef<uint8_t> Buf) {
|
||||
// 64-bit FNV1 hash
|
||||
const uint64_t Prime = 0x100000001b3;
|
||||
for (uint8_t B : Buf) {
|
||||
Hash *= Prime;
|
||||
Hash ^= B;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> void BuildIdSection<ELFT>::writeBuildId() {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
write64<E>(HashBuf, Hash);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
MipsReginfoOutputSection<ELFT>::MipsReginfoOutputSection()
|
||||
: OutputSectionBase<ELFT>(".reginfo", SHT_MIPS_REGINFO, SHF_ALLOC) {
|
||||
|
@ -1632,5 +1662,10 @@ template class SymbolTableSection<ELF32LE>;
|
|||
template class SymbolTableSection<ELF32BE>;
|
||||
template class SymbolTableSection<ELF64LE>;
|
||||
template class SymbolTableSection<ELF64BE>;
|
||||
|
||||
template class BuildIdSection<ELF32LE>;
|
||||
template class BuildIdSection<ELF32BE>;
|
||||
template class BuildIdSection<ELF64LE>;
|
||||
template class BuildIdSection<ELF64BE>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -511,12 +511,26 @@ private:
|
|||
std::vector<FdeData> FdeList;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class BuildIdSection final : public OutputSectionBase<ELFT> {
|
||||
public:
|
||||
BuildIdSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void update(ArrayRef<uint8_t> 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 <class ELFT> struct Out {
|
||||
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
|
||||
typedef typename llvm::object::ELFFile<ELFT>::Elf_Phdr Elf_Phdr;
|
||||
static BuildIdSection<ELFT> *BuildId;
|
||||
static DynamicSection<ELFT> *Dynamic;
|
||||
static EhFrameHeader<ELFT> *EhFrameHdr;
|
||||
static GnuHashTableSection<ELFT> *GnuHashTab;
|
||||
|
@ -541,6 +555,7 @@ template <class ELFT> struct Out {
|
|||
static OutputSectionBase<ELFT> *ProgramHeaders;
|
||||
};
|
||||
|
||||
template <class ELFT> BuildIdSection<ELFT> *Out<ELFT>::BuildId;
|
||||
template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
|
||||
template <class ELFT> EhFrameHeader<ELFT> *Out<ELFT>::EhFrameHdr;
|
||||
template <class ELFT> GnuHashTableSection<ELFT> *Out<ELFT>::GnuHashTab;
|
||||
|
|
|
@ -75,6 +75,7 @@ private:
|
|||
bool openFile();
|
||||
void writeHeader();
|
||||
void writeSections();
|
||||
void writeBuildId();
|
||||
bool isDiscarded(InputSectionBase<ELFT> *IS) const;
|
||||
StringRef getOutputSectionName(InputSectionBase<ELFT> *S) const;
|
||||
bool needsInterpSection() const {
|
||||
|
@ -143,6 +144,7 @@ template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
|
|||
ProgramHeaders.updateAlign(sizeof(uintX_t));
|
||||
|
||||
// Instantiate optional output sections if they are needed.
|
||||
std::unique_ptr<BuildIdSection<ELFT>> BuildId;
|
||||
std::unique_ptr<GnuHashTableSection<ELFT>> GnuHashTab;
|
||||
std::unique_ptr<GotPltSection<ELFT>> GotPlt;
|
||||
std::unique_ptr<HashTableSection<ELFT>> HashTab;
|
||||
|
@ -151,6 +153,8 @@ template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
|
|||
std::unique_ptr<SymbolTableSection<ELFT>> SymTabSec;
|
||||
std::unique_ptr<OutputSection<ELFT>> MipsRldMap;
|
||||
|
||||
if (Config->BuildId)
|
||||
BuildId.reset(new BuildIdSection<ELFT>);
|
||||
if (Config->GnuHash)
|
||||
GnuHashTab.reset(new GnuHashTableSection<ELFT>);
|
||||
if (Config->SysvHash)
|
||||
|
@ -175,6 +179,7 @@ template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
|
|||
MipsRldMap->updateAlign(sizeof(uintX_t));
|
||||
}
|
||||
|
||||
Out<ELFT>::BuildId = BuildId.get();
|
||||
Out<ELFT>::DynStrTab = &DynStrTab;
|
||||
Out<ELFT>::DynSymTab = &DynSymTab;
|
||||
Out<ELFT>::Dynamic = &Dynamic;
|
||||
|
@ -219,6 +224,7 @@ template <class ELFT> void Writer<ELFT>::run() {
|
|||
return;
|
||||
writeHeader();
|
||||
writeSections();
|
||||
writeBuildId();
|
||||
if (HasError)
|
||||
return;
|
||||
check(Buffer->commit());
|
||||
|
@ -1115,6 +1121,7 @@ template <class ELFT> void Writer<ELFT>::addPredefinedSections() {
|
|||
|
||||
// This order is not the same as the final output order
|
||||
// because we sort the sections using their attributes below.
|
||||
Add(Out<ELFT>::BuildId);
|
||||
Add(Out<ELFT>::SymTab);
|
||||
Add(Out<ELFT>::ShStrTab);
|
||||
Add(Out<ELFT>::StrTab);
|
||||
|
@ -1533,6 +1540,29 @@ template <class ELFT> void Writer<ELFT>::writeSections() {
|
|||
Sec->writeTo(Buf + Sec->getFileOff());
|
||||
}
|
||||
|
||||
template <class ELFT> void Writer<ELFT>::writeBuildId() {
|
||||
BuildIdSection<ELFT> *S = Out<ELFT>::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<ELFT> *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<ELF32LE>(SymbolTable<ELF32LE> *Symtab);
|
||||
template void elf::writeResult<ELF32BE>(SymbolTable<ELF32BE> *Symtab);
|
||||
template void elf::writeResult<ELF64LE>(SymbolTable<ELF64LE> *Symtab);
|
||||
|
|
|
@ -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:
|
Loading…
Reference in New Issue