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 @@ -921,6 +921,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 @@ -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 @@ -174,6 +174,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, @@ -1136,6 +1151,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); } +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, watermarkSize); // Descriptor size + write32(buf + 8, NT_LLVM_WATERMARK); // Type + memcpy(buf + 12, "LLVM\0\0\0", 8); // Name string + watermarkBuf = buf + 20; +} + + 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 @@ -75,6 +75,7 @@ void writeSections(); void writeSectionsBinary(); void writeBuildId(); + void writeWatermark(); std::unique_ptr &buffer; @@ -374,6 +375,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>(); @@ -605,6 +611,9 @@ if (errorCount()) return; + // Backfill the watermark section content. + writeWatermark(); + // Handle -Map and -cref options. writeMapFile(); writeCrossReferenceTable(); @@ -2662,9 +2671,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. @@ -2676,48 +2684,100 @@ hashFn(hashBuf.data(), hashes); } -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; - } +static void WriteHash(std::vector &hash, const size_t first, + const size_t last, size_t chunkSize, + const BuildIdKind hashKind) { + assert(first < last); - // 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)}; + std::vector> parts = + split(llvm::ArrayRef(Out::bufferStart + first, last - first), + chunkSize); - 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; + } + + std::vector buildId(mainPart->buildId->hashSize); + 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; + PhdrEntry *ProgramHeaderTable = nullptr; + + 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); + } else if (phdr->p_type == PT_PHDR) + ProgramHeaderTable = phdr; + } + + if (first >= last) + error("Unable to apply watermark because no PT_LOAD segments were found!"); + + // Exclude the ELF header and Program Header Table as some fields may be rewriiten + // by utilities such as objcopy. + first = std::max(sizeof(ELFT::Ehdr), first); + + 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; + } else { + error("--watermark requires that the Program Header Table is the first or " + "last segment!"); + } + } + + WriteHash(watermark, first, last, 1024 * 1024, BuildIdKind::Fast); + + for (Partition &part : partitions) + part.watermark->writeWatermark(watermark); +} + template void elf::createSyntheticSections(); template void elf::createSyntheticSections(); template void elf::createSyntheticSections(); 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-unknown-linux %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]}} 00001c 00 A 0 0 4 +# CONTENT: Hex dump of section '.note.llvm.watermark': +# CONTENT-NEXT: 0x00200158 05000000 08000000 04000000 4c4c564d ............LLVM +# CONTENT-NEXT: 0x00200168 00000000 fffc4de4 465a0b13 ......M.FZ.. + +.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 Index: llvm/test/tools/llvm-readobj/note-llvm.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/note-llvm.test @@ -0,0 +1,73 @@ +## Test tools are able to dump the LLVM Watermark note section. Also test the +## output when the type field is incorrect. + +# RUN: yaml2obj -docnum=1 %s > %t1.o +# RUN: llvm-readobj --notes %t1.o | FileCheck %s --check-prefix=LLVM +# RUN: llvm-readelf --notes %t1.o | FileCheck %s --check-prefix=GNU + +# LLVM: Notes [ +# LLVM-NEXT: NoteSection { +# LLVM-NEXT: Offset: 0x40 +# LLVM-NEXT: Size: 0x1C +# LLVM-NEXT: Note { +# LLVM-NEXT: Owner: LLVM +# LLVM-NEXT: Data size: 0x8 +# LLVM-NEXT: Type: NT_LLVM_WATERMARK (Loadable sections watermark) +# LLVM-NEXT: Watermark: 04851f07bebd4483 +# LLVM-NEXT: } +# LLVM-NEXT: } +# LLVM-NEXT: ] + +# GNU: Displaying notes found at file offset 0x00000040 with length 0x0000001c: +# GNU-NEXT: Owner Data size Description +# GNU-NEXT: LLVM 0x00000008 NT_LLVM_WATERMARK (Loadable sections watermark) +# GNU-NEXT: LLVM Watermark: 04851f07bebd4483 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .note.llvm.watermark + Type: SHT_NOTE + AddressAlign: 0x0000000000000004 + Size: 28 + Flags: [ SHF_ALLOC ] + Content: 0500000008000000040000004c4c564d0000000004851f07bebd4483 + + +# RUN: yaml2obj -docnum=2 %s > %t2.o +# RUN: llvm-readobj --notes %t2.o | FileCheck %s --check-prefix=LLVM_UNKNOWNTYPE +# RUN: llvm-readelf --notes %t2.o | FileCheck %s --check-prefix=GNU_UNKNOWNTYPE + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .note.llvm.watermark + Type: SHT_NOTE + AddressAlign: 0x0000000000000004 + Size: 28 + Flags: [ SHF_ALLOC ] + Content: 0500000008000000ff0000004c4c564d0000000004851f07bebd4483 + +# LLVM_UNKNOWNTYPE: Notes [ +# LLVM_UNKNOWNTYPE-NEXT: NoteSection { +# LLVM_UNKNOWNTYPE-NEXT: Offset: 0x40 +# LLVM_UNKNOWNTYPE-NEXT: Size: 0x1C +# LLVM_UNKNOWNTYPE-NEXT: Note { +# LLVM_UNKNOWNTYPE-NEXT: Owner: LLVM +# LLVM_UNKNOWNTYPE-NEXT: Data size: 0x8 +# LLVM_UNKNOWNTYPE-NEXT: Type: Unknown note type (0x000000ff) +# LLVM_UNKNOWNTYPE-NEXT: } +# LLVM_UNKNOWNTYPE-NEXT: } +# LLVM_UNKNOWNTYPE-NEXT: ] + +# GNU_UNKNOWNTYPE: Displaying notes found at file offset 0x00000040 with length 0x0000001c: +# GNU_UNKNOWNTYPE-NEXT: Owner Data size Description +# GNU_UNKNOWNTYPE-NEXT: LLVM 0x00000008 Unknown note type (0x000000ff) Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -4099,6 +4099,16 @@ return OS.str(); } +static std::string getLLVMNoteTypeName(const uint32_t NT) { + if (NT == NT_LLVM_WATERMARK) + return std::string("NT_LLVM_WATERMARK (Loadable sections watermark)"); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return OS.str(); +} + static std::string getFreeBSDNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; @@ -4434,6 +4444,30 @@ } } +template +static void printLLVMWatermarkLLVMStyle(uint32_t NoteType, + ArrayRef Desc, + ScopedPrinter &W) { + if (NoteType != NT_LLVM_WATERMARK) + return; + + const size_t HashSize = 8; + auto Hash = StringRef(reinterpret_cast(Desc.data()), HashSize); + W.printString("Watermark", toHex(Hash, true)); +} + +template +static void printLLVMWatermark(raw_ostream &OS, uint32_t NoteType, + ArrayRef Desc) { + + if (NoteType != NT_LLVM_WATERMARK) + return; + + const size_t HashSize = 8; + auto Hash = StringRef(reinterpret_cast(Desc.data()), HashSize); + OS << " LLVM Watermark: " << toHex(Hash, true); +} + struct CoreFileMapping { uint64_t Start, End, Offset; StringRef Filename; @@ -4531,6 +4565,8 @@ OS << getAMDNoteTypeName(Type) << '\n'; } else if (Name == "AMDGPU") { OS << getAMDGPUNoteTypeName(Type) << '\n'; + } else if (Name == "LLVM") { + OS << getLLVMNoteTypeName(Type) << '\n'; } else { StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE ? getCoreNoteTypeName(Type) @@ -4565,6 +4601,8 @@ else reportWarning(Note.takeError(), this->FileName); } + } else if (Name == "LLVM") { + printLLVMWatermark(OS, Type, Descriptor); } else if (!Descriptor.empty()) { OS << " description data:"; for (uint8_t B : Descriptor) @@ -5383,7 +5421,8 @@ this->dumper()->printSymbolsHelper(true); } -template void LLVMStyle::printDynamic(const ELFFile *Obj) { +template +void LLVMStyle::printDynamic(const ELFFile *Obj) { Elf_Dyn_Range Table = this->dumper()->dynamic_table(); if (Table.empty()) return; @@ -5738,6 +5777,8 @@ W.printString("Type", getAMDNoteTypeName(Type)); } else if (Name == "AMDGPU") { W.printString("Type", getAMDGPUNoteTypeName(Type)); + } else if (Name == "LLVM") { + W.printString("Type", getLLVMNoteTypeName(Type)); } else { StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE ? getCoreNoteTypeName(Type) @@ -5773,6 +5814,8 @@ else reportWarning(Note.takeError(), this->FileName); } + } else if (Name == "LLVM") { + printLLVMWatermarkLLVMStyle(Type, Descriptor, W); } else if (!Descriptor.empty()) { W.printBinaryBlock("Description data", Descriptor); }