From: Rui Ueyama Date: Wed, 5 Jun 2019 03:04:46 +0000 (+0000) Subject: Read .note.gnu.property sections and emit a merged .note.gnu.property section. X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2057f8366a40621f24209f886b1d95fb6fa25bdb;p=platform%2Fupstream%2Fllvm.git Read .note.gnu.property sections and emit a merged .note.gnu.property section. This patch also adds `--require-cet` option for the sake of testing. The actual feature for IBT-aware PLT is not included in this patch. This is a part of https://reviews.llvm.org/D59780. Submitting this first should make it easy to work with a related change (https://reviews.llvm.org/D62609). Differential Revision: https://reviews.llvm.org/D62853 llvm-svn: 362579 --- diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index ff48fd5..085674b 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -82,6 +82,7 @@ struct VersionDefinition { // Most fields are initialized by the driver. struct Configuration { uint8_t OSABI = 0; + uint32_t AndFeatures = 0; llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::StringMap SectionStartMap; llvm::StringRef Chroot; @@ -147,6 +148,7 @@ struct Configuration { bool ExportDynamic; bool FixCortexA53Errata843419; bool FormatBinary = false; + bool RequireCET; bool GcSections; bool GdbIndex; bool GnuHash = false; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index b49ab85..a90522a 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -334,6 +334,9 @@ static void checkOptions() { if (Config->SingleRoRx && !Script->HasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } + + if (Config->ZRetpolineplt && Config->RequireCET) + error("--require-cet may not be used with -z retpolineplt"); } static const char *getReproduceOption(opt::InputArgList &Args) { @@ -813,6 +816,7 @@ static void readConfigs(opt::InputArgList &Args) { Config->FilterList = args::getStrings(Args, OPT_filter); Config->Fini = Args.getLastArgValue(OPT_fini, "_fini"); Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419); + Config->RequireCET = Args.hasArg(OPT_require_cet); Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); @@ -1584,6 +1588,30 @@ static void wrapSymbols(ArrayRef Wrapped) { Symtab->wrap(W.Sym, W.Real, W.Wrap); } +// To enable CET (x86's hardware-assited control flow enforcement), each +// source file must be compiled with -fcf-protection. Object files compiled +// with the flag contain feature flags indicating that they are compatible +// with CET. We enable the feature only when all object files are compatible +// with CET. +// +// This function returns the merged feature flags. If 0, we cannot enable CET. +// +// Note that the CET-aware PLT is not implemented yet. We do error +// check only. +template static uint32_t getAndFeatures() { + if (Config->EMachine != EM_386 && Config->EMachine != EM_X86_64) + return 0; + + uint32_t Ret = -1; + for (InputFile *F : ObjectFiles) { + uint32_t Features = cast>(F)->AndFeatures; + if (!Features && Config->RequireCET) + error(toString(F) + ": --require-cet: file is not compatible with CET"); + Ret &= Features; + } + return Ret; +} + static const char *LibcallRoutineNames[] = { #define HANDLE_LIBCALL(code, name) name, #include "llvm/IR/RuntimeLibcalls.def" @@ -1762,6 +1790,10 @@ template void LinkerDriver::link(opt::InputArgList &Args) { (S->Name.startswith(".debug") || S->Name.startswith(".zdebug")); }); + // Read .note.gnu.property sections from input object files which + // contain a hint to tweak linker's and loader's behaviors. + Config->AndFeatures = getAndFeatures(); + Config->EFlags = Target->calcEFlags(); // MaxPageSize (sometimes called abi page size) is the maximum page size that // the output can be run on. For example if the OS can use 4k or 64k page diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 1699142..e16a07f 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -25,6 +25,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -34,6 +35,7 @@ using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::sys::fs; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -753,6 +755,68 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { } } +// If a source file is compiled with x86 hardware-assisted call flow control +// enabled, the generated object file contains feature flags indicating that +// fact. This function reads the feature flags and returns it. +// +// Essentially we want to read a single 32-bit value in this function, but this +// function is rather complicated because the value is buried deep inside a +// .note.gnu.property section. +// +// The section consists of one or more NOTE records. Each NOTE record consists +// of zero or more type-length-value fields. We want to find a field of a +// certain type. It seems a bit too much to just store a 32-bit value, perhaps +// the ABI is unnecessarily complicated. +template +static uint32_t readAndFeatures(ObjFile *Obj, ArrayRef Data) { + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::Note; + + while (!Data.empty()) { + // Read one NOTE record. + if (Data.size() < sizeof(Elf_Nhdr)) + fatal(toString(Obj) + ": .note.gnu.property: section too short"); + + auto *Nhdr = reinterpret_cast(Data.data()); + if (Data.size() < Nhdr->getSize()) + fatal(toString(Obj) + ": .note.gnu.property: section too short"); + + Elf_Note Note(*Nhdr); + if (Nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || Note.getName() != "GNU") { + Data = Data.slice(Nhdr->getSize()); + continue; + } + + // Read a body of a NOTE record, which consists of type-length-value fields. + ArrayRef Desc = Note.getDesc(); + while (!Desc.empty()) { + if (Desc.size() < 8) + fatal(toString(Obj) + ": .note.gnu.property: section too short"); + + uint32_t Type = read32le(Desc.data()); + uint32_t Size = read32le(Desc.data() + 4); + + if (Type == GNU_PROPERTY_X86_FEATURE_1_AND) { + // We found the field. + return read32le(Desc.data() + 8); + } + + // On 64-bit, a payload may be followed by a 4-byte padding to make its + // size a multiple of 8. + if (ELFT::Is64Bits) + Size = alignTo(Size, 8); + + Desc = Desc.slice(Size + 8); // +8 for Type and Size + } + + // Go to next NOTE record if a note section didn't contain + // X86_FEATURES_1_AND description. + Data = Data.slice(Nhdr->getSize()); + } + + return 0; +} + template InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &Sec) { uint32_t Idx = Sec.sh_info; @@ -901,6 +965,19 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { if (Name == ".note.GNU-stack") return &InputSection::Discarded; + // If an object file is compatible with Intel Control-Flow Enforcement + // Technology (CET), it has a .note.gnu.property section containing the + // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag. + // + // Since we merge bitmaps from multiple object files to create a new + // .note.gnu.property containing a single AND'ed bitmap, we discard an input + // file's .note.gnu.property section. + if (Name == ".note.gnu.property") { + ArrayRef Contents = check(this->getObj().getSectionContents(&Sec)); + this->AndFeatures = readAndFeatures(this, Contents); + return &InputSection::Discarded; + } + // Split stacks is a feature to support a discontiguous stack, // commonly used in the programming language Go. For the details, // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index e506b9e..64cd020 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -228,6 +228,8 @@ public: // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. uint32_t MipsGp0 = 0; + uint32_t AndFeatures = 0; + // Name of source file obtained from STT_FILE symbol value, // or empty string if there is no such symbol in object file // symbol table. diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 6db47e6..d89eaea 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -171,6 +171,10 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"">; def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">, HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">; +// This option is intentionally hidden from the user as the implementation +// is not complete. +def require_cet: F<"require-cet">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 9606eed..f461761 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -288,6 +288,35 @@ static size_t getHashSize() { } } +// This class represents a linker-synthesized .note.gnu.property section. +// +// In x86, object files may contain feature flags indicating the features that +// they are using. The flags are stored in a .note.gnu.property section. +// +// lld reads the sections from input files and merges them by computing AND of +// the flags. The result is written as a new .note.gnu.property section. +// +// If the flag is zero (which indicates that the intersection of the feature +// sets is empty, or some input files didn't have .note.gnu.property sections), +// we don't create this section. +GnuPropertySection::GnuPropertySection() + : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, 4, + ".note.gnu.property") {} + +void GnuPropertySection::writeTo(uint8_t *Buf) { + write32(Buf, 4); // Name size + write32(Buf + 4, Config->Is64 ? 16 : 12); // Content size + write32(Buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type + memcpy(Buf + 12, "GNU", 4); // Name string + write32(Buf + 16, GNU_PROPERTY_X86_FEATURE_1_AND); // Feature type + write32(Buf + 20, 4); // Feature size + write32(Buf + 24, Config->AndFeatures); // Feature flags + if (Config->Is64) + write32(Buf + 28, 0); // Padding +} + +size_t GnuPropertySection::getSize() const { return Config->Is64 ? 32 : 28; } + BuildIdSection::BuildIdSection() : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), HashSize(getHashSize()) {} diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index ad37cde..786ca4e 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -147,6 +147,13 @@ public: size_t getSize() const override { return 0; } }; +class GnuPropertySection : public SyntheticSection { +public: + GnuPropertySection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override; +}; + // .note.gnu.build-id section. class BuildIdSection : public SyntheticSection { // First 16 bytes are a header. diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 40d32a8..2ebf844 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -433,6 +433,9 @@ template static void createSyntheticSections() { In.Iplt = make(true); Add(In.Iplt); + if (Config->AndFeatures) + Add(make()); + // .note.GNU-stack is always added when we are creating a re-linkable // object file. Other linkers are using the presence of this marker // section to control the executable-ness of the stack area, but that diff --git a/lld/test/ELF/Inputs/i386-cet1.s b/lld/test/ELF/Inputs/i386-cet1.s new file mode 100644 index 0000000..62cd66e --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet1.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.align 4 +.long 4 +.long 12 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/i386-cet2.s b/lld/test/ELF/Inputs/i386-cet2.s new file mode 100644 index 0000000..1ccf701 --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet2.s @@ -0,0 +1,20 @@ +.section ".note.gnu.property", "a" +.align 4 +.long 4 +.long 24 +.long 5 +.asciz "GNU" + +.long 0xc0000000 +.long 4 +.long 0 + +.long 0xc0000002 +.long 4 +.long 3 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/i386-cet3.s b/lld/test/ELF/Inputs/i386-cet3.s new file mode 100644 index 0000000..cdec62a --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet3.s @@ -0,0 +1,5 @@ +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/i386-cet4.s b/lld/test/ELF/Inputs/i386-cet4.s new file mode 100644 index 0000000..6d07eea --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet4.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.align 4 +.long 4 +.long 12 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 0xfffffffd + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet1.s b/lld/test/ELF/Inputs/x86-64-cet1.s new file mode 100644 index 0000000..ca0a368 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet1.s @@ -0,0 +1,17 @@ +.section ".note.gnu.property", "a" +.align 4 +.long 4 +.long 16 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet2.s b/lld/test/ELF/Inputs/x86-64-cet2.s new file mode 100644 index 0000000..39a6291 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet2.s @@ -0,0 +1,22 @@ +.section ".note.gnu.property", "a" +.align 4 +.long 4 +.long 32 +.long 5 +.asciz "GNU" + +.long 0xc0000000 +.long 4 +.long 0 +.long 0 + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet3.s b/lld/test/ELF/Inputs/x86-64-cet3.s new file mode 100644 index 0000000..cdec62a --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet3.s @@ -0,0 +1,5 @@ +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet4.s b/lld/test/ELF/Inputs/x86-64-cet4.s new file mode 100644 index 0000000..85ddee2 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet4.s @@ -0,0 +1,17 @@ +.section ".note.gnu.property", "a" +.align 4 +.long 4 +.long 16 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 0xfffffffd +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/i386-cet.s b/lld/test/ELF/i386-cet.s new file mode 100644 index 0000000..22a5205 --- /dev/null +++ b/lld/test/ELF/i386-cet.s @@ -0,0 +1,48 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-cet4.s -o %t4.o + +# RUN: ld.lld -e func1 %t.o %t1.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# RUN: ld.lld -e func1 %t.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# CET: Properties: x86 feature: IBT, SHSTK + +# RUN: ld.lld -e func1 %t.o %t3.o -o %t +# RUN: llvm-readelf -S %t | FileCheck -check-prefix=NOCET %s + +# NOCET: Section Headers +# NOCET-NOT: .note.gnu.property + +# RUN: not ld.lld -e func1 %t.o %t3.o -o %t --require-cet 2>&1 \ +# RUN: | FileCheck -check-prefix=ERROR %s +# ERROR: i386-cet.s.tmp3.o: --require-cet: file is not compatible with CET + +# RUN: ld.lld -e func1 %t.o %t4.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=NOSHSTK -match-full-lines %s + +# Check .note.gnu.protery without property SHSTK. +# NOSHSTK: Properties: x86 feature: IBT + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func1 +.type func1,@function +func1: + call func2 + ret diff --git a/lld/test/ELF/x86-64-cet.s b/lld/test/ELF/x86-64-cet.s new file mode 100644 index 0000000..3fb3716 --- /dev/null +++ b/lld/test/ELF/x86-64-cet.s @@ -0,0 +1,48 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet4.s -o %t4.o + +# RUN: ld.lld -e func1 %t.o %t1.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# RUN: ld.lld -e func1 %t.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# CET: Properties: x86 feature: IBT, SHSTK + +# RUN: ld.lld -e func1 %t.o %t3.o -o %t +# RUN: llvm-readelf -S %t | FileCheck -check-prefix=NOCET %s + +# NOCET: Section Headers +# NOCET-NOT: .note.gnu.property + +# RUN: not ld.lld -e func1 %t.o %t3.o -o %t --require-cet 2>&1 \ +# RUN: | FileCheck -check-prefix=ERROR %s +# ERROR: x86-64-cet.s.tmp3.o: --require-cet: file is not compatible with CET + +# RUN: ld.lld -e func1 %t.o %t4.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=NOSHSTK -match-full-lines %s + +# Check .note.gnu.protery without property SHSTK. +# NOSHSTK: Properties: x86 feature: IBT + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func1 +.type func1,@function +func1: + call func2 + ret diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 45bbd6c..5552208 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -592,9 +592,9 @@ class Elf_Note_Impl { template friend class Elf_Note_Iterator_Impl; +public: Elf_Note_Impl(const Elf_Nhdr_Impl &Nhdr) : Nhdr(Nhdr) {} -public: /// Get the note's name, excluding the terminating null byte. StringRef getName() const { if (!Nhdr.n_namesz)