Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -34,7 +34,7 @@ ELF64BEKind }; -// For --build-id. +// For --build-id and watermark. enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; // For --discard-{all,locals,none}. @@ -191,6 +191,7 @@ bool warnIfuncTextrel; bool warnMissingEntry; bool warnSymbolOrdering; + bool watermark; bool writeAddends; bool zCombreloc; bool zCopyreloc; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -918,6 +918,7 @@ args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); config->warnSymbolOrdering = args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); + config->watermark = args.hasArg(OPT_watermark); config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true); config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true); config->zExecstack = getZFlag(args, "execstack", "noexecstack", false); Index: lld/ELF/InputFiles.cpp =================================================================== --- lld/ELF/InputFiles.cpp +++ lld/ELF/InputFiles.cpp @@ -1020,12 +1020,12 @@ name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx") return &InputSection::discarded; - // If we are creating a new .build-id section, strip existing .build-id - // sections so that the output won't have more than one .build-id. + // If we are creating a new .build-id section or watermark, strip existing + // sections so that the output won't have more than one. // This is not usually a problem because input object files normally don't - // have .build-id sections, but you can create such files by - // "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it. - if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None) + // have .build-id sections or watermark, but you can create such files by + // "ld.{bfd,gold,lld} -r --build-id/--watermark", and we want to guard against it. + if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None || name == ".note.llvm.watermark") return &InputSection::discarded; // The linker merges EH (exception handling) frames and creates a Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -419,6 +419,10 @@ def visual_studio_diagnostics_format : F<"vs-diagnostics">, HelpText<"Format diagnostics for Visual Studio compatiblity">; +defm watermark : B<"watermark", + "Enable the computation of a hash for loadable sections", + "Disable the computation of a hash for loadable sections">; + // Aliases def: Separate<["-"], "f">, Alias, HelpText<"Alias for --auxiliary">; def: F<"call_shared">, Alias, HelpText<"Alias for --Bdynamic">; Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -172,6 +172,21 @@ uint8_t *hashBuf; }; +// .note.llvm-watermark section. +class WatermarkSection : public SyntheticSection { + static const unsigned headerSize = 20; + +public: + const size_t watermarkSize = 8; + WatermarkSection(); + void writeTo(uint8_t *buf) override; + size_t getSize() const override { return headerSize + watermarkSize; } + void writeWatermark(llvm::ArrayRef buf); + + private: + uint8_t *watermarkBuf; +}; + // BssSection is used to reserve space for copy relocations and common symbols. // We create three instances of this class for .bss, .bss.rel.ro and "COMMON", // that are used for writable symbols, read-only symbols and common symbols, @@ -1143,6 +1158,7 @@ VersionDefinitionSection *verDef; SyntheticSection *verNeed; VersionTableSection *verSym; + WatermarkSection *watermark; unsigned getNumber() const { return this - &partitions[0] + 1; } }; Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -339,6 +339,24 @@ memcpy(hashBuf, buf.data(), hashSize); } +lld::elf::WatermarkSection::WatermarkSection() +:SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.llvm.watermark") +{} + +void lld::elf::WatermarkSection::writeTo(uint8_t *buf) { + write32(buf, 5); // Name size + write32(buf + 4, watermarkSize); // Content size + write32(buf + 8, NT_LLVM_WATERMARK); // Type + memcpy(buf + 12, "LLVM\0\0\0", 8); // Name string + watermarkBuf = buf + 20; +} + + void lld::elf::WatermarkSection::writeWatermark(llvm::ArrayRef buf) { + assert(buf.size() == watermarkSize); + memcpy(watermarkBuf, buf.data(), watermarkSize); +} + + BssSection::BssSection(StringRef name, uint64_t size, uint32_t alignment) : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, alignment, name) { this->bss = true; Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -76,6 +76,7 @@ void writeSections(); void writeSectionsBinary(); void writeBuildId(); + void writeWatermark(); std::unique_ptr &buffer; @@ -377,6 +378,11 @@ add(part.buildId); } + if (config->watermark) { + part.watermark = make(); + add(part.watermark); + } + part.dynStrTab = make(".dynstr", true); part.dynSymTab = make>(*part.dynStrTab); part.dynamic = make>(); @@ -633,6 +639,9 @@ if (errorCount()) return; + // Compute and fill the watermark note. + writeWatermark(); + // Handle -Map and -cref options. writeMapFile(); writeCrossReferenceTable(); @@ -2619,9 +2628,8 @@ // of the hash values. static void computeHash(llvm::MutableArrayRef hashBuf, - llvm::ArrayRef data, + const std::vector> &chunks, std::function arr)> hashFn) { - std::vector> chunks = split(data, 1024 * 1024); std::vector hashes(chunks.size() * hashBuf.size()); // Compute hash values. @@ -2633,48 +2641,87 @@ hashFn(hashBuf.data(), hashes); } -template void Writer::writeBuildId() { - if (!mainPart->buildId || !mainPart->buildId->getParent()) - return; +static void WriteHash(std::vector &hash, const size_t first, + const size_t last, size_t chunkSize, + const BuildIdKind hashKind) { + assert(first < last); - if (config->buildId == BuildIdKind::Hexstring) { - for (Partition &part : partitions) - part.buildId->writeBuildId(config->buildIdVector); - return; - } + std::vector> parts = + split(llvm::ArrayRef(Out::bufferStart + first, last - first), + chunkSize); - // Compute a hash of all sections of the output file. - size_t hashSize = mainPart->buildId->hashSize; - std::vector buildId(hashSize); - llvm::ArrayRef buf{Out::bufferStart, size_t(fileSize)}; - - switch (config->buildId) { + switch (hashKind) { case BuildIdKind::Fast: - computeHash(buildId, buf, [](uint8_t *dest, ArrayRef arr) { + computeHash(hash, parts, [](uint8_t *dest, ArrayRef arr) { write64le(dest, xxHash64(arr)); }); break; case BuildIdKind::Md5: - computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef arr) { - memcpy(dest, MD5::hash(arr).data(), hashSize); + computeHash(hash, parts, [&](uint8_t *dest, ArrayRef arr) { + memcpy(dest, MD5::hash(arr).data(), hash.size()); }); break; case BuildIdKind::Sha1: - computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef arr) { - memcpy(dest, SHA1::hash(arr).data(), hashSize); + computeHash(hash, parts, [&](uint8_t *dest, ArrayRef arr) { + memcpy(dest, SHA1::hash(arr).data(), hash.size()); }); break; case BuildIdKind::Uuid: - if (auto ec = llvm::getRandomBytes(buildId.data(), hashSize)) + if (auto ec = llvm::getRandomBytes(hash.data(), hash.size())) error("entropy source failure: " + ec.message()); break; default: - llvm_unreachable("unknown BuildIdKind"); + llvm_unreachable("unknown HashKind"); } + +} + +template void Writer::writeBuildId() { + if (!mainPart->buildId || !mainPart->buildId->getParent()) + return; + + if (config->buildId == BuildIdKind::Hexstring) { + for (Partition &part : partitions) + part.buildId->writeBuildId(config->buildIdVector); + return; + } + + size_t hashSize = mainPart->buildId->hashSize; + std::vector buildId(hashSize); + llvm::ArrayRef buf{Out::bufferStart, size_t(fileSize)}; + std::vector> parts = {buf}; + + WriteHash(buildId, 0, fileSize, 1024*1024, config->buildId); + for (Partition &part : partitions) part.buildId->writeBuildId(buildId); } +template void Writer::writeWatermark() { + if (!mainPart->watermark || !mainPart->watermark->getParent()) + return; + + const size_t hashSize = mainPart->watermark->watermarkSize; + std::vector watermark(hashSize); + size_t first = SIZE_MAX; + size_t last = 0; + + for (PhdrEntry *phdr : mainPart->phdrs) { + if (phdr->p_type == PT_LOAD) { + first = std::min(first, phdr->p_offset); + last = std::max(last, phdr->p_offset + phdr->p_filesz); + } + } + + if (first > last) + return; + + WriteHash(watermark, first, last, 1024 * 1024, BuildIdKind::Fast); + + for (Partition &part : partitions) + part.watermark->writeWatermark(watermark); +} + template void elf::writeResult(); template void elf::writeResult(); template void elf::writeResult(); Index: lld/test/ELF/watermark.s =================================================================== --- /dev/null +++ lld/test/ELF/watermark.s @@ -0,0 +1,35 @@ +## Test that a watermark is generated placed in the correct section with the +## correct alignment when using --watermark. Check also that the watermark can +## be disabled with --no-watermark and that watermark is disabled by default. + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t.default +# RUN: llvm-readobj -S %t.default | FileCheck -check-prefix=NOWATERMARK %s +# RUN: ld.lld --no-watermark %t -o %t.nowatermark +# RUN: llvm-readobj -S %t.nowatermark | FileCheck -check-prefix=NOWATERMARK %s + +# NOWATERMARK-NOT: Name: .note.llvm.watermark + +# RUN: ld.lld --watermark %t -o %t.watermark +# RUN: llvm-readobj -S %t.watermark | FileCheck -check-prefix=SECTION %s +# RUN: llvm-objdump -s %t.watermark | FileCheck --strict-whitespace -check-prefix=CONTENT %s + +# SECTION: Name: .note.llvm.watermark +# SECTION-NEXT: Type: SHT_NOTE +# SECTION-NEXT: Flags [ +# SECTION-NEXT: SHF_ALLOC +# SECTION-NEXT: ] +# SECTION-NEXT: Address: +# SECTION-NEXT: Offset: [[_:0x[0-9A-Z]*(0|4|8|C)$]] +# SECTION-NEXT: Size: 28 +# SECTION-NEXT: Link: +# SECTION-NEXT: Info: +# SECTION-NEXT: AddressAlignment: 4 + +# CONTENT: Contents of section .note.llvm.watermark: +# CONTENT-NEXT: 200158 05000000 08000000 04000000 4c4c564d ............LLVM +# CONTENT-NEXT: 200168 00000000 617d9ca1 6fbf4336 ....a}..o.C6 + +.globl _start +_start: +nop Index: llvm/include/llvm/BinaryFormat/ELF.h =================================================================== --- llvm/include/llvm/BinaryFormat/ELF.h +++ llvm/include/llvm/BinaryFormat/ELF.h @@ -1419,6 +1419,7 @@ // LLVM-specific notes. enum { NT_LLVM_HWASAN_GLOBALS = 3, + NT_LLVM_WATERMARK = 4, }; // GNU note types