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
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 7d14596..6bacbd3 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -75,6 +75,7 @@
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 @@
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 @@
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 @@
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 @@
return;
writeHeader();
writeSections();
+ writeBuildId();
if (HasError)
return;
check(Buffer->commit());
@@ -1115,6 +1121,7 @@
// 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 @@
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);