forked from OSchip/llvm-project
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
This commit is contained in:
parent
aade782a98
commit
2057f8366a
|
@ -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<uint64_t> 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;
|
||||
|
|
|
@ -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<WrappedSymbol> 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 <class ELFT> 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<ObjFile<ELFT>>(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 <class ELFT> 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<ELFT>();
|
||||
|
||||
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
|
||||
|
|
|
@ -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 <class ELFT>
|
||||
static uint32_t readAndFeatures(ObjFile<ELFT> *Obj, ArrayRef<uint8_t> 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<const Elf_Nhdr *>(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<uint8_t> 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 <class ELFT>
|
||||
InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
uint32_t Idx = Sec.sh_info;
|
||||
|
@ -901,6 +965,19 @@ InputSectionBase *ObjFile<ELFT>::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<uint8_t> 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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -171,6 +171,10 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
|
|||
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]">;
|
||||
|
||||
|
|
|
@ -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()) {}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -433,6 +433,9 @@ template <class ELFT> static void createSyntheticSections() {
|
|||
In.Iplt = make<PltSection>(true);
|
||||
Add(In.Iplt);
|
||||
|
||||
if (Config->AndFeatures)
|
||||
Add(make<GnuPropertySection>());
|
||||
|
||||
// .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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
.text
|
||||
.globl func2
|
||||
.type func2,@function
|
||||
func2:
|
||||
ret
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
.text
|
||||
.globl func2
|
||||
.type func2,@function
|
||||
func2:
|
||||
ret
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -592,9 +592,9 @@ class Elf_Note_Impl {
|
|||
|
||||
template <class NoteIteratorELFT> friend class Elf_Note_Iterator_Impl;
|
||||
|
||||
public:
|
||||
Elf_Note_Impl(const Elf_Nhdr_Impl<ELFT> &Nhdr) : Nhdr(Nhdr) {}
|
||||
|
||||
public:
|
||||
/// Get the note's name, excluding the terminating null byte.
|
||||
StringRef getName() const {
|
||||
if (!Nhdr.n_namesz)
|
||||
|
|
Loading…
Reference in New Issue