Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -84,6 +84,7 @@ uint8_t OSABI = 0; llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::StringMap SectionStartMap; + llvm::StringRef BuildIdLinkDir; llvm::StringRef Chroot; llvm::StringRef DynamicLinker; llvm::StringRef DwoDir; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -730,6 +730,7 @@ Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->BuildIdLinkDir = Args.getLastArgValue(OPT_build_id_link_dir); Config->CheckSections = Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); Config->Chroot = Args.getLastArgValue(OPT_chroot); Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -32,6 +32,11 @@ def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">, MetaVarName<"[fast,md5,sha,uuid,0x]">; +defm build_id_link_dir: + Eq<"build-id-link-dir", + "Hard-link output to /xx/xxx name derived from hex build ID">, + MetaVarName<"">; + defm check_sections: B<"check-sections", "Check section addresses for overlaps (default)", "Do not check section addresses for overlaps">; Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -153,7 +153,7 @@ BuildIdSection(); void writeTo(uint8_t *Buf) override; size_t getSize() const override { return HeaderSize + HashSize; } - void writeBuildId(llvm::ArrayRef Buf); + llvm::SmallVector writeBuildId(llvm::ArrayRef Buf); private: void computeHash(llvm::ArrayRef Buf, Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -331,7 +331,8 @@ this->Size = Size; } -void BuildIdSection::writeBuildId(ArrayRef Buf) { +llvm::SmallVector +BuildIdSection::writeBuildId(ArrayRef Buf) { switch (Config->BuildId) { case BuildIdKind::Fast: computeHash(Buf, [](uint8_t *Dest, ArrayRef Arr) { @@ -358,6 +359,7 @@ default: llvm_unreachable("unknown BuildIdKind"); } + return {HashBuf, HashBuf + HashSize}; } EhFrameSection::EhFrameSection() Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -25,6 +25,8 @@ #include "lld/Common/Threads.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" #include using namespace llvm; @@ -71,6 +73,7 @@ void writeSections(); void writeSectionsBinary(); void writeBuildId(); + void writeBuildIdLink(); std::unique_ptr &Buffer; @@ -83,6 +86,8 @@ uint64_t FileSize; uint64_t SectionHeaderOff; + + SmallVector BuildIdBytes; }; } // anonymous namespace @@ -522,6 +527,8 @@ if (auto E = Buffer->commit()) error("failed to write to the output file: " + toString(std::move(E))); + + writeBuildIdLink(); } static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName, @@ -2411,7 +2418,38 @@ // Compute a hash of all sections of the output file. uint8_t *Start = Buffer->getBufferStart(); uint8_t *End = Start + FileSize; - InX::BuildId->writeBuildId({Start, End}); + BuildIdBytes = InX::BuildId->writeBuildId({Start, End}); +} + +template void Writer::writeBuildIdLink() { + if (Config->BuildIdLinkDir.empty() || + !InX::BuildId || !InX::BuildId->getParent()) + return; + + if (BuildIdBytes.size() < 2) { + error("build ID too small for --build-id-link-dir"); + return; + } + + SmallString<128> Path(Config->BuildIdLinkDir); + llvm::sys::path::append(Path, llvm::toHex(BuildIdBytes[0], true)); + if (auto EC = sys::fs::create_directories(Path)) { + error("cannot create build ID link directory " + + Path + ": " + EC.message()); + return; + } + + llvm::sys::path::append(Path, llvm::toHex( + ArrayRef(&BuildIdBytes.begin()[1], BuildIdBytes.end()), true)); + auto EC = llvm::sys::fs::create_hard_link(Config->OutputFile, Path); + if (EC) { + // Hard linking failed, try to remove the file first if it exists. + if (llvm::sys::fs::exists(Path)) + llvm::sys::fs::remove(Path, true); + EC = llvm::sys::fs::create_hard_link(Config->OutputFile, Path); + if (EC) + error("cannot link " + Path + ": " + EC.message()); + } } template void elf::writeResult();