Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -199,6 +199,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 @@ -963,6 +963,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.hasFlag(OPT_watermark, OPT_no_watermark, false); 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 @@ -954,12 +954,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 @@ -426,6 +426,10 @@ def visual_studio_diagnostics_format : F<"vs-diagnostics">, HelpText<"Format diagnostics for Visual Studio compatibility">; +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 @@ -174,6 +174,22 @@ uint8_t *hashBuf; }; +// .note.llvm-watermark section. +class WatermarkSection : public SyntheticSection { + static const unsigned headerSize = 20; + +public: + const size_t watermarkSize = 8; + const uint32_t watermarkVersion = 1u; + WatermarkSection(); + void writeTo(uint8_t *buf) override; + size_t getSize() const override { return headerSize + sizeof(watermarkVersion) + 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, @@ -1134,6 +1150,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 @@ -336,6 +336,25 @@ memcpy(hashBuf, buf.data(), hashSize); } +WatermarkSection::WatermarkSection() +:SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.llvm.watermark") +{} + +void WatermarkSection::writeTo(uint8_t *buf) { + write32(buf, 5); // Name size + write32(buf + 4, sizeof(watermarkVersion) + watermarkSize); // Descriptor size + write32(buf + 8, NT_LLVM_WATERMARK); // Type + memcpy(buf + 12, "LLVM\0\0\0", 8); // Name string + write32(buf + 20, watermarkVersion); // Version + watermarkBuf = buf + 24; +} + + void WatermarkSection::writeWatermark(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 @@ -29,6 +29,8 @@ #include "llvm/Support/SHA1.h" #include "llvm/Support/xxhash.h" #include +#include + using namespace llvm; using namespace llvm::ELF; @@ -38,6 +40,7 @@ namespace lld { namespace elf { + namespace { // The writer writes a SymbolTable result to a file. template class Writer { @@ -75,6 +78,7 @@ void writeSections(); void writeSectionsBinary(); void writeBuildId(); + void writeWatermark(); std::unique_ptr &buffer; @@ -385,6 +389,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>(); @@ -610,6 +619,9 @@ if (errorCount()) return; + // Backfill the watermark section content. + writeWatermark(); + // Handle -Map and -cref options. writeMapFile(); writeCrossReferenceTable(); @@ -2740,6 +2752,125 @@ part.buildId->writeBuildId(buildId); } +std::tuple CropHeaderBytes(PhdrEntry *programHeader, + PhdrEntry *programHeaderTable, + size_t elfHeaderSize) { + size_t last = programHeader->p_offset + programHeader->p_memsz; + size_t first = std::max(elfHeaderSize, programHeader->p_offset); + if (programHeaderTable != nullptr) { + if (programHeaderTable->p_offset == first) { + first += programHeaderTable->p_filesz; + } else if (programHeaderTable->p_offset + programHeaderTable->p_filesz == + last) { + last -= programHeaderTable->p_filesz; + } + } + return std::make_tuple(first, last); +} + +static void computeWatermark(llvm::MutableArrayRef hashDest, + const size_t first, const size_t last, + const uint8_t *data) { + assert(first < last); + + std::vector> inputChunks = + split(llvm::ArrayRef(data + first, last - first), 1024 * 1024); + std::vector chunkHashes(inputChunks.size() * hashDest.size()); + + parallelForEachN(0, inputChunks.size(), [&](size_t i) { + write64le(chunkHashes.data() + i * hashDest.size(), xxHash64(inputChunks[i])); + }); + + write64le(hashDest.data(), xxHash64(chunkHashes)); +} + +static void OmitWatermarkSection(OutputSection *watermarkNote, + std::vector &ptLoadHeaders) { + size_t sectionFirst = watermarkNote->offset; + size_t sectionLast = sectionFirst + watermarkNote->size; + + for (int i = 0; i < ptLoadHeaders.size(); i++) { + PhdrEntry *pHdr = ptLoadHeaders[i]; + size_t segmentFirst = pHdr->p_offset; + size_t segmentLast = segmentFirst + pHdr->p_filesz; + + if (sectionFirst >= segmentLast || sectionLast <= segmentFirst) + continue; + + if (sectionFirst >= segmentFirst) { + size_t segmentSize = pHdr->p_filesz; + pHdr->p_filesz = sectionFirst - segmentFirst; + + // Section begins and ends within segment. + if (sectionLast < segmentLast) { + PhdrEntry *postSection = new PhdrEntry(PT_LOAD, 0); + postSection->p_offset = sectionLast; + postSection->p_filesz = + segmentSize - pHdr->p_filesz - watermarkNote->size; + ptLoadHeaders.insert(ptLoadHeaders.begin() + i, postSection); + } + } else { // Section ends within segment or at segment end. + pHdr->p_offset = std::min(segmentLast, sectionLast); + pHdr->p_filesz = + (sectionLast >= segmentLast) ? 0 : segmentLast - sectionLast; + } + } +} + +template void Writer::writeWatermark() { + if (!mainPart->watermark || !mainPart->watermark->getParent()) + return; + + const size_t hashSize = mainPart->watermark->watermarkSize; + PhdrEntry *ProgramHeaderTable = nullptr; + std::vector loadableSegments; + + for (PhdrEntry *phdr : mainPart->phdrs) { + if (phdr->p_type == PT_LOAD) { + loadableSegments.push_back(phdr); + } else if (phdr->p_type == PT_PHDR) + ProgramHeaderTable = phdr; + } + + if (loadableSegments.empty()) + error("Unable to apply watermark because no PT_LOAD segments were found!"); + + // Omit the watermark note section bytes from the watermark computation. + if (OutputSection *watermarkSection = findSection(".note.llvm.watermark")) + OmitWatermarkSection(watermarkSection, loadableSegments); + + // Calculate the watermark based on a virtual address ordering of segments. + // The watermark is then dependant on the memory image and not the ordering + // within the file. + std::sort(loadableSegments.begin(), loadableSegments.end(), + [](const PhdrEntry *a, const PhdrEntry *b) { + return a->p_vaddr < b->p_vaddr; + }); + + std::vector loadableSegmentWatermarks(loadableSegments.size() * + hashSize); + + parallelForEachN(0, loadableSegments.size(), [&](size_t i) { + size_t first, last; + std::tie(first, last) = CropHeaderBytes( + loadableSegments[i], ProgramHeaderTable, + sizeof(typename ELFT::Ehdr)); // Ensure we don't include the program + // header table or ELF header, as these + // may be altered by tools such as + // objcopy. + if (first < last) + computeWatermark(loadableSegmentWatermarks[i * hashSize], first, last, + Out::bufferStart); + }); + + std::vector finalWatermark(hashSize); + computeWatermark(finalWatermark, 0, loadableSegmentWatermarks.size(), + loadableSegmentWatermarks.data()); + + for (Partition &part : partitions) + part.watermark->writeWatermark(finalWatermark); +} + template void createSyntheticSections(); template void createSyntheticSections(); template void createSyntheticSections(); @@ -2751,4 +2882,4 @@ template void writeResult(); } // namespace elf -} // namespace lld +} // namespace lld \ No newline at end of file Index: lld/test/ELF/watermark.s =================================================================== --- /dev/null +++ lld/test/ELF/watermark.s @@ -0,0 +1,24 @@ +## Test that a watermark is 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 %s -o %t +# RUN: ld.lld %t -o %t.default +# RUN: llvm-readelf -S %t.default | FileCheck -check-prefix=NOWATERMARK %s +# RUN: ld.lld --no-watermark %t -o %t.nowatermark +# RUN: llvm-readelf -S %t.nowatermark | FileCheck -check-prefix=NOWATERMARK %s + +# NOWATERMARK-NOT: Name: .note.llvm.watermark + +# RUN: ld.lld --watermark %t -o %t.watermark +# RUN: llvm-readelf -S %t.watermark | FileCheck -check-prefix=SECTION %s +# RUN: llvm-readelf -x .note.llvm.watermark %t.watermark | FileCheck --strict-whitespace -check-prefix=CONTENT %s + +# SECTION: .note.llvm.watermark NOTE {{[0-9a-f]+}} {{[0-9a-f]+[048b]}} 000020 00 A 0 0 4 +# CONTENT: Hex dump of section '.note.llvm.watermark': +# CONTENT-NEXT: 0x00200158 05000000 0c000000 04000000 4c4c564d ............LLVM +# CONTENT-NEXT: 0x00200168 00000000 01000000 1c6639d1 55f3734e .........f9.U.sN + +.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