Index: llvm/test/tools/llvm-readobj/elf-watermark.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/elf-watermark.test @@ -0,0 +1,20 @@ +## Test that we can calculate and print the loadable segment watermark for an +## elf file and ensure this watermark matches that computed by lld. +## lld writes the watermark to a ".note.llvm.watermark" section which is placed +## in a PT_NOTE segment. +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld --watermark %t -o %t.watermark +# RUN: llvm-readelf -x .note.llvm.watermark %t.watermark | FileCheck --strict-whitespace -check-prefix=DUMP %s +# RUN: llvm-readobj --compute-watermark %t.watermark | FileCheck --strict-whitespace -check-prefix=COMPUTE %s + +# DUMP: Hex dump of section '.note.llvm.watermark': +# DUMP-NEXT: 05000000 0c000000 04000000 4c4c564d ............LLVM +# DUMP-NEXT: 00000000 01000000 f9ceaa42 d8d7016d ...........B...m + +# COMPUTE: Computed loadable segments watermark { +# COMPUTE-NEXT: Version: 1 +# COMPUTE-NEXT: Value: 0x6D01D7D842AACEF9 + +.globl _start +_start: +nop Index: llvm/test/tools/llvm-readobj/note-llvm.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/note-llvm.test @@ -0,0 +1,116 @@ +## Test tools are able to dump the LLVM Watermark note section. Also test the +## output when the type field is incorrect. +## Show also that if the watermark is mapped to a PT_NOTE segment that the +## watermark can be dumped even if llvm-objcopy --strip-sections has been used. + +# 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: 0x20 +# LLVM-NEXT: Note { +# LLVM-NEXT: Owner: LLVM +# LLVM-NEXT: Data size: 0xC +# LLVM-NEXT: Type: NT_LLVM_WATERMARK (Loadable sections watermark) +# LLVM-NEXT: Version: 01000000 +# LLVM-NEXT: Watermark: 45e69f81a671eaac +# LLVM-NEXT: } +# LLVM-NEXT: } +# LLVM-NEXT: ] + +# GNU: Displaying notes found at file offset 0x00000040 with length 0x00000020: +# GNU-NEXT: Owner Data size Description +# GNU-NEXT: LLVM 0x0000000c NT_LLVM_WATERMARK (Loadable sections watermark) +# GNU-NEXT: LLVM Watermark: 45e69f81a671eaac Version: 0x01000000 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .note.llvm.watermark + Type: SHT_NOTE + AddressAlign: 0x0000000000000004 + Size: 32 + Content: 050000000c000000040000004c4c564d000000000100000045e69f81a671eaac + + +# 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: 32 + Content: 050000000c000000ff0000004c4c564d000000000100000045e69f81a671eaac + +# LLVM_UNKNOWNTYPE: Notes [ +# LLVM_UNKNOWNTYPE-NEXT: NoteSection { +# LLVM_UNKNOWNTYPE-NEXT: Offset: 0x40 +# LLVM_UNKNOWNTYPE-NEXT: Size: 0x20 +# LLVM_UNKNOWNTYPE-NEXT: Note { +# LLVM_UNKNOWNTYPE-NEXT: Owner: LLVM +# LLVM_UNKNOWNTYPE-NEXT: Data size: 0xC +# 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 0x00000020: +# GNU_UNKNOWNTYPE-NEXT: Owner Data size Description +# GNU_UNKNOWNTYPE-NEXT: LLVM 0x0000000c Unknown note type (0x000000ff) + +# RUN: yaml2obj -docnum=3 %s > %t3.o +# RUN: llvm-objcopy --strip-sections %t3.o %t3.stripped.o +# RUN: llvm-readobj --notes %t3.stripped.o | FileCheck %s --check-prefix=LLVM_STRIPPED +# RUN: llvm-readelf --notes %t3.stripped.o | FileCheck %s --check-prefix=GNU_STRIPPED + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 + Entry: 0x0000000000400000 +Sections: + - Name: .note.llvm.watermark + Type: SHT_NOTE + AddressAlign: 0x0000000000000004 + Size: 32 + Content: 050000000c000000040000004c4c564d0000000001000000beeeeeeeeeeeeeef +ProgramHeaders: + - Type: PT_NOTE + Sections: + - Section: .note.llvm.watermark + +# LLVM_STRIPPED: Notes [ +# LLVM_STRIPPED-NEXT: NoteSection { +# LLVM_STRIPPED-NEXT: Offset: 0x40 +# LLVM_STRIPPED-NEXT: Size: 0x20 +# LLVM_STRIPPED-NEXT: Note { +# LLVM_STRIPPED-NEXT: Owner: LLVM +# LLVM_STRIPPED-NEXT: Data size: 0xC +# LLVM_STRIPPED-NEXT: Type: NT_LLVM_WATERMARK (Loadable sections watermark) +# LLVM_STRIPPED-NEXT: Version: 01000000 +# LLVM_STRIPPED-NEXT: Watermark: beeeeeeeeeeeeeef +# LLVM_STRIPPED-NEXT: } +# LLVM_STRIPPED-NEXT: } +# LLVM_STRIPPED-NEXT: ] + +# GNU_STRIPPED: Displaying notes found at file offset 0x00000040 with length 0x00000020: +# GNU_STRIPPED-NEXT: Owner Data size Description +# GNU_STRIPPED-NEXT: LLVM 0x0000000c NT_LLVM_WATERMARK (Loadable sections watermark) +# GNU_STRIPPED-NEXT: LLVM Watermark: beeeeeeeeeeeeeef Version: 0x01000000 Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -39,6 +39,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Object/RelocationResolver.h" #include "llvm/Object/StackMapParser.h" +#include "llvm/Object/Watermark.h" #include "llvm/Support/AMDGPUMetadata.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" @@ -221,6 +222,7 @@ void printELFLinkerOptions() override; void printStackSizes() override; + void printWatermark() override; const object::ELFObjectFile *getElfObject() const { return ObjF; }; @@ -639,6 +641,7 @@ virtual void printNotes(const ELFFile *Obj) = 0; virtual void printELFLinkerOptions(const ELFFile *Obj) = 0; virtual void printStackSizes(const ELFObjectFile *Obj) = 0; + virtual void printWatermark(const ELFObjectFile *Obj) = 0; void printNonRelocatableStackSizes(const ELFObjectFile *Obj, std::function PrintHeader); void printRelocatableStackSizes(const ELFObjectFile *Obj, @@ -706,6 +709,7 @@ void printELFLinkerOptions(const ELFFile *Obj) override; void printStackSizes(const ELFObjectFile *Obj) override; void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printWatermark(const ELFObjectFile *Obj) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; void printMipsABIFlags(const ELFObjectFile *Obj) override; @@ -824,6 +828,7 @@ void printELFLinkerOptions(const ELFFile *Obj) override; void printStackSizes(const ELFObjectFile *Obj) override; void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printWatermark(const ELFObjectFile *Obj) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; void printMipsABIFlags(const ELFObjectFile *Obj) override; @@ -2089,6 +2094,10 @@ ELFDumperStyle->printStackSizes(ObjF); } +template void ELFDumper::printWatermark() { + ELFDumperStyle->printWatermark(ObjF); +} + #define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ { #enum, prefix##_##enum } @@ -4368,6 +4377,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; @@ -4703,6 +4722,35 @@ } } +template +static void printLLVMWatermarkLLVMStyle(uint32_t NoteType, + ArrayRef Desc, + ScopedPrinter &W) { + if (NoteType != NT_LLVM_WATERMARK) + return; + + // Currently there is only one version of the watermark and it has a hash size + // of 8. + auto Version = StringRef(reinterpret_cast(Desc.data()), 4); + auto Hash = StringRef(reinterpret_cast(Desc.data() + 4), 8); + W.printString("Version", toHex(Version, true)); + W.printString("Watermark", toHex(Hash, true)); +} + +template +static void printLLVMWatermark(raw_ostream &OS, uint32_t NoteType, + ArrayRef Desc) { + if (NoteType != NT_LLVM_WATERMARK) + return; + + // Currently there is only one version of the watermark and it has a hash size + // of 8. + auto Version = StringRef(reinterpret_cast(Desc.data()), 4); + auto Hash = StringRef(reinterpret_cast(Desc.data() + 4), 8); + OS << " LLVM Watermark: " << toHex(Hash, true) << " Version: 0x" + << toHex(Version); +} + struct CoreFileMapping { uint64_t Start, End, Offset; StringRef Filename; @@ -4800,6 +4848,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) @@ -4833,6 +4883,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) @@ -4868,6 +4920,64 @@ } } +// Calculate the loadable sections watermark. If the file contains +// a watermark section this can be viewed with --notes. +template +static Expected> +computeWatermark(StringRef FileName, const ELFObjectFile *Obj, watermark::Watermarker& W) { + llvm::ArrayRef pHdrs = + unwrapOrError(FileName, Obj->getELFFile()->program_headers()); + + std::vector segmentInfo = + W.extractSegmentInfo(pHdrs); + if (segmentInfo.empty()) + return createError( + "unable to compute watermark because no PT_LOAD segments were found"); + + size_t programHeaderTableOffset = 0; + size_t programHeaderTableSize = 0; + auto pHdrIt = std::find_if(pHdrs.begin(), pHdrs.end(), [](ELFT::Phdr pHdr) { + return pHdr.p_type == PT_PHDR; + }); + + if (pHdrIt != pHdrs.end()) { + programHeaderTableOffset = pHdrIt->p_offset; + programHeaderTableSize = pHdrIt->p_filesz; + } + + return W.computeWatermark( + segmentInfo, sizeof(typename ELFT::Ehdr), programHeaderTableOffset, + programHeaderTableSize, Obj->getELFFile()->base()); +} + +template +void GNUStyle::printWatermark(const ELFObjectFile *Obj) { + watermark::Watermarker W; + std::vector watermark = + unwrapOrError(this->FileName, computeWatermark(this->FileName, Obj, W)); + uint64_t *watermarkVal = (uint64_t *)watermark.data(); + OS << "Computed loadable segments watermark\n"; + OS << "Version"; + OS.PadToColumn(10); + OS << "Value\n"; + OS << W.getVersion(); + OS.PadToColumn(10); + OS << to_hexString(*watermarkVal); +} + +template +void LLVMStyle::printWatermark(const ELFObjectFile *Obj) { + watermark::Watermarker M; + std::vector watermark = + unwrapOrError(this->FileName, computeWatermark(this->FileName, Obj, M)); + uint64_t *watermarkVal = (uint64_t *)watermark.data(); + DictScope SS(W, "Computed loadable segments watermark"); + { + W.printNumber("Version", M.getVersion()); + W.printHex("Value", *watermarkVal); + } +} + template void GNUStyle::printELFLinkerOptions(const ELFFile *Obj) { OS << "printELFLinkerOptions not implemented!\n"; @@ -6033,6 +6143,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) @@ -6067,6 +6179,8 @@ else reportWarning(Note.takeError(), this->FileName); } + } else if (Name == "LLVM") { + printLLVMWatermarkLLVMStyle(Type, Descriptor, W); } else if (!Descriptor.empty()) { W.printBinaryBlock("Description data", Descriptor); } Index: llvm/tools/llvm-readobj/ObjDumper.h =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.h +++ llvm/tools/llvm-readobj/ObjDumper.h @@ -69,6 +69,7 @@ virtual void printNotes() {} virtual void printELFLinkerOptions() {} virtual void printStackSizes() {} + virtual void printWatermark() {} virtual void printArchSpecificInfo() { } // Only implemented for PE/COFF. Index: llvm/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -125,6 +125,10 @@ cl::opt Notes("notes", cl::desc("Display the ELF notes in the file")); cl::alias NotesShort("n", cl::desc("Alias for --notes"), cl::aliasopt(Notes)); + // --compute-watermark, -cw + cl::opt ComputeWatermark("compute-watermark", cl::desc("Compute the loadable sections watermark")); + cl::alias ComputeWatermarkShort("cw", cl::desc("Alias for --compute-watermark"), cl::aliasopt(ComputeWatermark)); + // --dyn-relocations cl::opt DynRelocs("dyn-relocations", cl::desc("Display the dynamic relocation entries in the file")); @@ -503,6 +507,8 @@ Dumper->printAddrsig(); if (opts::Notes) Dumper->printNotes(); + if(opts::ComputeWatermark) + Dumper->printWatermark(); } if (Obj->isCOFF()) { if (opts::COFFImports)