forked from OSchip/llvm-project
[COFF] Add minimal support for /guard:cf
Summary: This patch adds some initial support for Windows control flow guard. At the end of the day, the linker needs to synthesize a table of RVAs very similar to the structured exception handler table (/safeseh). Both /safeseh and /guard:cf take sections of symbol table indices (.sxdata and .gfids$y) and turn them into RVA tables referenced by the load config struct in the CRT through special symbols. Reviewers: ruiu, amccarth Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D42592 llvm-svn: 324306
This commit is contained in:
parent
dc51fb4919
commit
af2f7da74c
|
@ -453,12 +453,14 @@ void LocalImportChunk::writeTo(uint8_t *Buf) const {
|
|||
}
|
||||
}
|
||||
|
||||
void SEHTableChunk::writeTo(uint8_t *Buf) const {
|
||||
void RVATableChunk::writeTo(uint8_t *Buf) const {
|
||||
ulittle32_t *Begin = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
|
||||
size_t Cnt = 0;
|
||||
for (Defined *D : Syms)
|
||||
Begin[Cnt++] = D->getRVA();
|
||||
for (const ChunkAndOffset &CO : Syms)
|
||||
Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset;
|
||||
std::sort(Begin, Begin + Cnt);
|
||||
assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt &&
|
||||
"RVA tables should be de-duplicated");
|
||||
}
|
||||
|
||||
// Windows-specific. This class represents a block in .reloc section.
|
||||
|
|
|
@ -320,17 +320,41 @@ private:
|
|||
Defined *Sym;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// A chunk for SEH table which contains RVAs of safe exception handler
|
||||
// functions. x86-only.
|
||||
class SEHTableChunk : public Chunk {
|
||||
// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
|
||||
// offset into the chunk. Order does not matter as the RVA table will be sorted
|
||||
// later.
|
||||
struct ChunkAndOffset {
|
||||
Chunk *InputChunk;
|
||||
uint32_t Offset;
|
||||
|
||||
struct DenseMapInfo {
|
||||
static ChunkAndOffset getEmptyKey() {
|
||||
return {llvm::DenseMapInfo<Chunk *>::getEmptyKey(), 0};
|
||||
}
|
||||
static ChunkAndOffset getTombstoneKey() {
|
||||
return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0};
|
||||
}
|
||||
static unsigned getHashValue(const ChunkAndOffset &CO) {
|
||||
return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue(
|
||||
{CO.InputChunk, CO.Offset});
|
||||
}
|
||||
static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) {
|
||||
return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;
|
||||
|
||||
// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
|
||||
class RVATableChunk : public Chunk {
|
||||
public:
|
||||
explicit SEHTableChunk(std::set<Defined *> S) : Syms(std::move(S)) {}
|
||||
explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {}
|
||||
size_t getSize() const override { return Syms.size() * 4; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
std::set<Defined *> Syms;
|
||||
SymbolRVASet Syms;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
|
@ -362,4 +386,10 @@ void applyBranch24T(uint8_t *Off, int32_t V);
|
|||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
namespace llvm {
|
||||
template <>
|
||||
struct DenseMapInfo<lld::coff::ChunkAndOffset>
|
||||
: lld::coff::ChunkAndOffset::DenseMapInfo {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -112,6 +112,9 @@ struct Configuration {
|
|||
|
||||
bool SaveTemps = false;
|
||||
|
||||
// /guard:cf
|
||||
bool GuardCF;
|
||||
|
||||
// Used for SafeSEH.
|
||||
Symbol *SEHTable = nullptr;
|
||||
Symbol *SEHCount = nullptr;
|
||||
|
|
|
@ -983,6 +983,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
if (auto *Arg = Args.getLastArg(OPT_stack))
|
||||
parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit);
|
||||
|
||||
// Handle /guard:cf
|
||||
if (auto *Arg = Args.getLastArg(OPT_guard))
|
||||
parseGuard(Arg->getValue());
|
||||
|
||||
// Handle /heap
|
||||
if (auto *Arg = Args.getLastArg(OPT_heap))
|
||||
parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit);
|
||||
|
@ -1285,11 +1289,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
Symtab->addAbsolute("___safe_se_handler_count", 0);
|
||||
}
|
||||
|
||||
// We do not support /guard:cf (control flow protection) yet.
|
||||
// Define CFG symbols anyway so that we can link MSVC 2015 CRT.
|
||||
Symtab->addAbsolute(mangle("__guard_fids_count"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_fids_table"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_flags"), 0x100);
|
||||
Symtab->addAbsolute(mangle("__guard_flags"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_iat_count"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_iat_table"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
|
||||
|
@ -1364,7 +1366,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
// Handle /safeseh.
|
||||
if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
|
||||
for (ObjFile *File : ObjFile::Instances)
|
||||
if (!File->SEHCompat)
|
||||
if (!File->hasSafeSEH())
|
||||
error("/safeseh: " + File->getName() + " is not compatible with SEH");
|
||||
if (errorCount())
|
||||
return;
|
||||
|
|
|
@ -145,6 +145,8 @@ StringRef machineToStr(MachineTypes MT);
|
|||
// Parses a string in the form of "<integer>[,<integer>]".
|
||||
void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr);
|
||||
|
||||
void parseGuard(StringRef Arg);
|
||||
|
||||
// Parses a string in the form of "<integer>[.<integer>]".
|
||||
// Minor's default value is 0.
|
||||
void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor);
|
||||
|
|
|
@ -128,6 +128,15 @@ void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) {
|
|||
fatal("invalid number: " + S2);
|
||||
}
|
||||
|
||||
void parseGuard(StringRef Arg) {
|
||||
if (Arg.equals_lower("no"))
|
||||
Config->GuardCF = false;
|
||||
else if (Arg.equals_lower("cf"))
|
||||
Config->GuardCF = true;
|
||||
else
|
||||
fatal("invalid argument to /GUARD: " + Arg);
|
||||
}
|
||||
|
||||
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
|
||||
void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
|
||||
uint32_t *Minor) {
|
||||
|
|
|
@ -151,15 +151,7 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
|
|||
if (auto EC = COFFObj->getSectionName(Sec, Name))
|
||||
fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
|
||||
EC.message());
|
||||
if (Name == ".sxdata") {
|
||||
ArrayRef<uint8_t> Data;
|
||||
COFFObj->getSectionContents(Sec, Data);
|
||||
if (Data.size() % 4 != 0)
|
||||
fatal(".sxdata must be an array of symbol table indices");
|
||||
SXData = {reinterpret_cast<const ulittle32_t *>(Data.data()),
|
||||
Data.size() / 4};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Name == ".drectve") {
|
||||
ArrayRef<uint8_t> Data;
|
||||
COFFObj->getSectionContents(Sec, Data);
|
||||
|
@ -191,6 +183,10 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
|
|||
// linked in the regular manner.
|
||||
if (C->isCodeView())
|
||||
DebugChunks.push_back(C);
|
||||
else if (Config->GuardCF && Name == ".gfids$y")
|
||||
GuardFidChunks.push_back(C);
|
||||
else if (Name == ".sxdata")
|
||||
SXDataChunks.push_back(C);
|
||||
else
|
||||
Chunks.push_back(C);
|
||||
|
||||
|
@ -308,10 +304,8 @@ Optional<Symbol *> ObjFile::createDefined(
|
|||
// Skip special symbols.
|
||||
if (Name == "@comp.id")
|
||||
return nullptr;
|
||||
// COFF spec 5.10.1. The .sxdata section.
|
||||
if (Name == "@feat.00") {
|
||||
if (Sym.getValue() & 1)
|
||||
SEHCompat = true;
|
||||
Feat00Flags = Sym.getValue();
|
||||
return nullptr;
|
||||
}
|
||||
if (Sym.isExternal())
|
||||
|
|
|
@ -110,6 +110,8 @@ public:
|
|||
MachineTypes getMachineType() override;
|
||||
ArrayRef<Chunk *> getChunks() { return Chunks; }
|
||||
ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
|
||||
ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; }
|
||||
ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; }
|
||||
ArrayRef<Symbol *> getSymbols() { return Symbols; }
|
||||
|
||||
// Returns a Symbol object for the SymbolIndex'th symbol in the
|
||||
|
@ -123,13 +125,17 @@ public:
|
|||
|
||||
static std::vector<ObjFile *> Instances;
|
||||
|
||||
// True if this object file is compatible with SEH.
|
||||
// COFF-specific and x86-only.
|
||||
bool SEHCompat = false;
|
||||
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
||||
// indicate if an object was compiled with certain security features enabled
|
||||
// like stack guard, safeseh, /guard:cf, or other things.
|
||||
uint32_t Feat00Flags = 0;
|
||||
|
||||
// The symbol table indexes of the safe exception handlers.
|
||||
// COFF-specific and x86-only.
|
||||
ArrayRef<llvm::support::ulittle32_t> SXData;
|
||||
// True if this object file is compatible with SEH. COFF-specific and
|
||||
// x86-only. COFF spec 5.10.1. The .sxdata section.
|
||||
bool hasSafeSEH() { return Feat00Flags & 0x1; }
|
||||
|
||||
// True if this file was compiled with /guard:cf.
|
||||
bool hasGuardCF() { return Feat00Flags & 0x800; }
|
||||
|
||||
// Pointer to the PDB module descriptor builder. Various debug info records
|
||||
// will reference object files by "module index", which is here. Things like
|
||||
|
@ -165,6 +171,14 @@ private:
|
|||
// CodeView debug info sections.
|
||||
std::vector<SectionChunk *> DebugChunks;
|
||||
|
||||
// Chunks containing symbol table indices of exception handlers. Only used for
|
||||
// 32-bit x86.
|
||||
std::vector<SectionChunk *> SXDataChunks;
|
||||
|
||||
// Chunks containing symbol table indices of address taken symbols. These are
|
||||
// not linked into the final binary when /guard:cf is set.
|
||||
std::vector<SectionChunk *> GuardFidChunks;
|
||||
|
||||
// This vector contains the same chunks as Chunks, but they are
|
||||
// indexed such that you can get a SectionChunk by section index.
|
||||
// Nonexistent section indices are filled with null pointers.
|
||||
|
|
|
@ -28,6 +28,7 @@ def errorlimit : P<"errorlimit",
|
|||
def export : P<"export", "Export a function">;
|
||||
// No help text because /failifmismatch is not intended to be used by the user.
|
||||
def failifmismatch : P<"failifmismatch", "">;
|
||||
def guard : P<"guard", "Control flow guard">;
|
||||
def heap : P<"heap", "Size of the heap">;
|
||||
def ignore : P<"ignore", "Specify warning codes to ignore">;
|
||||
def implib : P<"implib", "Import library name">;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/RandomNumberGenerator.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
@ -123,6 +124,12 @@ private:
|
|||
void openFile(StringRef OutputPath);
|
||||
template <typename PEHeaderTy> void writeHeader();
|
||||
void createSEHTable(OutputSection *RData);
|
||||
void createGFIDTable(OutputSection *RData);
|
||||
void markSymbolsForRVATable(ObjFile *File,
|
||||
ArrayRef<SectionChunk *> SymIdxChunks,
|
||||
SymbolRVASet &TableSymbols);
|
||||
void maybeAddRVATable(OutputSection *RData, SymbolRVASet TableSymbols,
|
||||
StringRef TableSym, StringRef CountSym);
|
||||
void setSectionPermissions();
|
||||
void writeSections();
|
||||
void writeBuildId();
|
||||
|
@ -146,7 +153,8 @@ private:
|
|||
IdataContents Idata;
|
||||
DelayLoadContents DelayIdata;
|
||||
EdataContents Edata;
|
||||
SEHTableChunk *SEHTable = nullptr;
|
||||
RVATableChunk *GuardFidsTable = nullptr;
|
||||
RVATableChunk *SEHTable = nullptr;
|
||||
|
||||
Chunk *DebugDirectory = nullptr;
|
||||
std::vector<Chunk *> DebugRecords;
|
||||
|
@ -428,7 +436,13 @@ void Writer::createMiscChunks() {
|
|||
RData->addChunk(C);
|
||||
}
|
||||
|
||||
createSEHTable(RData);
|
||||
// Create SEH table. x86-only.
|
||||
if (Config->Machine == I386)
|
||||
createSEHTable(RData);
|
||||
|
||||
// Create the guard function id table if requested.
|
||||
if (Config->GuardCF)
|
||||
createGFIDTable(RData);
|
||||
}
|
||||
|
||||
// Create .idata section for the DLL-imported symbol table.
|
||||
|
@ -720,6 +734,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
|||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
|
||||
if (!Config->AllowIsolation)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
|
||||
if (Config->GuardCF)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;
|
||||
if (Config->Machine == I386 && !SEHTable &&
|
||||
!Symtab->findUnderscore("_load_config_used"))
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
|
||||
|
@ -825,34 +841,146 @@ void Writer::openFile(StringRef Path) {
|
|||
}
|
||||
|
||||
void Writer::createSEHTable(OutputSection *RData) {
|
||||
// Create SEH table. x86-only.
|
||||
if (Config->Machine != I386)
|
||||
return;
|
||||
|
||||
std::set<Defined *> Handlers;
|
||||
|
||||
SymbolRVASet Handlers;
|
||||
for (ObjFile *File : ObjFile::Instances) {
|
||||
if (!File->SEHCompat)
|
||||
// FIXME: We should error here instead of earlier unless /safeseh:no was
|
||||
// passed.
|
||||
if (!File->hasSafeSEH())
|
||||
return;
|
||||
for (uint32_t I : File->SXData)
|
||||
if (Symbol *B = File->getSymbol(I))
|
||||
if (B->isLive())
|
||||
Handlers.insert(cast<Defined>(B));
|
||||
|
||||
markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers);
|
||||
}
|
||||
|
||||
if (Handlers.empty())
|
||||
maybeAddRVATable(RData, std::move(Handlers), "__safe_se_handler_table",
|
||||
"__safe_se_handler_count");
|
||||
}
|
||||
|
||||
// Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set
|
||||
// cannot contain duplicates. Therefore, the set is uniqued by Chunk and the
|
||||
// symbol's offset into that Chunk.
|
||||
static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) {
|
||||
Chunk *C = S->getChunk();
|
||||
if (auto *SC = dyn_cast<SectionChunk>(C))
|
||||
C = SC->Repl; // Look through ICF replacement.
|
||||
uint32_t Off = S->getRVA() - (C ? C->getRVA() : 0);
|
||||
RVASet.insert({C, Off});
|
||||
}
|
||||
|
||||
// Visit all relocations from all section contributions of this object file and
|
||||
// mark the relocation target as address-taken.
|
||||
static void markSymbolsWithRelocations(ObjFile *File,
|
||||
SymbolRVASet &UsedSymbols) {
|
||||
for (Chunk *C : File->getChunks()) {
|
||||
// We only care about live section chunks. Common chunks and other chunks
|
||||
// don't generally contain relocations.
|
||||
SectionChunk *SC = dyn_cast<SectionChunk>(C);
|
||||
if (!SC || !SC->isLive())
|
||||
continue;
|
||||
|
||||
// Look for relocations in this section against symbols in executable output
|
||||
// sections.
|
||||
for (Symbol *Ref : SC->symbols()) {
|
||||
// FIXME: Do further testing to see if the relocation type matters,
|
||||
// especially for 32-bit where taking the address of something usually
|
||||
// uses an absolute relocation instead of a relative one.
|
||||
if (auto *D = dyn_cast_or_null<Defined>(Ref)) {
|
||||
Chunk *RefChunk = D->getChunk();
|
||||
OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
|
||||
if (OS && OS->getPermissions() & IMAGE_SCN_MEM_EXECUTE)
|
||||
addSymbolToRVASet(UsedSymbols, D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the guard function id table. This is a table of RVAs of all
|
||||
// address-taken functions. It is sorted and uniqued, just like the safe SEH
|
||||
// table.
|
||||
void Writer::createGFIDTable(OutputSection *RData) {
|
||||
SymbolRVASet AddressTakenSyms;
|
||||
for (ObjFile *File : ObjFile::Instances) {
|
||||
// If the object was compiled with /guard:cf, the address taken symbols are
|
||||
// in the .gfids$y sections. Otherwise, we approximate the set of address
|
||||
// taken symbols by checking which symbols were used by relocations in live
|
||||
// sections.
|
||||
if (File->hasGuardCF())
|
||||
markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms);
|
||||
else
|
||||
markSymbolsWithRelocations(File, AddressTakenSyms);
|
||||
}
|
||||
|
||||
// Mark the image entry as address-taken.
|
||||
if (Config->Entry)
|
||||
addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry));
|
||||
|
||||
maybeAddRVATable(RData, std::move(AddressTakenSyms), "__guard_fids_table",
|
||||
"__guard_fids_count");
|
||||
|
||||
// Set __guard_flags, which will be used in the load config to indicate that
|
||||
// /guard:cf was enabled.
|
||||
uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) |
|
||||
uint32_t(coff_guard_flags::HasFidTable);
|
||||
Symbol *FlagSym = Symtab->findUnderscore("__guard_flags");
|
||||
cast<DefinedAbsolute>(FlagSym)->setVA(GuardFlags);
|
||||
}
|
||||
|
||||
// Take a list of input sections containing symbol table indices and add those
|
||||
// symbols to an RVA table. The challenge is that symbol RVAs are not known and
|
||||
// depend on the table size, so we can't directly build a set of integers.
|
||||
void Writer::markSymbolsForRVATable(ObjFile *File,
|
||||
ArrayRef<SectionChunk *> SymIdxChunks,
|
||||
SymbolRVASet &TableSymbols) {
|
||||
for (SectionChunk *C : SymIdxChunks) {
|
||||
// Skip sections discarded by linker GC. This comes up when a .gfids section
|
||||
// is associated with something like a vtable and the vtable is discarded.
|
||||
// In this case, the associated gfids section is discarded, and we don't
|
||||
// mark the virtual member functions as address-taken by the vtable.
|
||||
if (!C->isLive())
|
||||
continue;
|
||||
|
||||
// Validate that the contents look like symbol table indices.
|
||||
ArrayRef<uint8_t> Data = C->getContents();
|
||||
if (Data.size() % 4 != 0) {
|
||||
warn("ignoring " + C->getSectionName() +
|
||||
" symbol table index section in object " + toString(File));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read each symbol table index and check if that symbol was included in the
|
||||
// final link. If so, add it to the table symbol set.
|
||||
ArrayRef<ulittle32_t> SymIndices(
|
||||
reinterpret_cast<const ulittle32_t *>(Data.data()), Data.size() / 4);
|
||||
ArrayRef<Symbol *> ObjSymbols = File->getSymbols();
|
||||
for (uint32_t SymIndex : SymIndices) {
|
||||
if (SymIndex >= ObjSymbols.size()) {
|
||||
warn("ignoring invalid symbol table index in section " +
|
||||
C->getSectionName() + " in object " + toString(File));
|
||||
continue;
|
||||
}
|
||||
if (Symbol *S = ObjSymbols[SymIndex]) {
|
||||
if (S->isLive())
|
||||
addSymbolToRVASet(TableSymbols, cast<Defined>(S));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the absolute table symbol with a synthetic symbol pointing to
|
||||
// TableChunk so that we can emit base relocations for it and resolve section
|
||||
// relative relocations.
|
||||
void Writer::maybeAddRVATable(OutputSection *RData,
|
||||
SymbolRVASet TableSymbols,
|
||||
StringRef TableSym, StringRef CountSym) {
|
||||
if (TableSymbols.empty())
|
||||
return;
|
||||
|
||||
SEHTable = make<SEHTableChunk>(Handlers);
|
||||
RData->addChunk(SEHTable);
|
||||
RVATableChunk *TableChunk = make<RVATableChunk>(std::move(TableSymbols));
|
||||
RData->addChunk(TableChunk);
|
||||
|
||||
// Replace the absolute table symbol with a synthetic symbol pointing to the
|
||||
// SEHTable chunk so that we can emit base relocations for it and resolve
|
||||
// section relative relocations.
|
||||
Symbol *T = Symtab->find("___safe_se_handler_table");
|
||||
Symbol *C = Symtab->find("___safe_se_handler_count");
|
||||
replaceSymbol<DefinedSynthetic>(T, T->getName(), SEHTable);
|
||||
cast<DefinedAbsolute>(C)->setVA(SEHTable->getSize() / 4);
|
||||
Symbol *T = Symtab->findUnderscore(TableSym);
|
||||
Symbol *C = Symtab->findUnderscore(CountSym);
|
||||
replaceSymbol<DefinedSynthetic>(T, T->getName(), TableChunk);
|
||||
cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4);
|
||||
}
|
||||
|
||||
// Handles /section options to allow users to overwrite
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
# RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
|
||||
# RUN: lld-link %t.obj -opt:noref -guard:cf -out:%t.exe -entry:main 2>&1 | FileCheck %s --check-prefix=ERRS
|
||||
# RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s
|
||||
|
||||
# ERRS: warning: ignoring .gfids$y symbol table index section in object {{.*}}gfids-corrupt{{.*}}
|
||||
# ERRS: warning: ignoring invalid symbol table index in section .gfids$y in object {{.*}}gfids-corrupt{{.*}}
|
||||
|
||||
# The table is arbitrary, really.
|
||||
# CHECK: ImageBase: 0x140000000
|
||||
# CHECK: LoadConfig [
|
||||
# CHECK: SEHandlerTable: 0x0
|
||||
# CHECK: SEHandlerCount: 0
|
||||
# CHECK: GuardCFCheckFunction: 0x0
|
||||
# CHECK: GuardCFCheckDispatch: 0x0
|
||||
# CHECK: GuardCFFunctionTable: 0x14000{{.*}}
|
||||
# CHECK: GuardCFFunctionCount: 2
|
||||
# CHECK: GuardFlags: 0x500
|
||||
# CHECK: GuardAddressTakenIatEntryTable: 0x0
|
||||
# CHECK: GuardAddressTakenIatEntryCount: 0
|
||||
# CHECK: GuardLongJumpTargetTable: 0x0
|
||||
# CHECK: GuardLongJumpTargetCount: 0
|
||||
# CHECK: ]
|
||||
# CHECK: GuardFidTable [
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: ]
|
||||
|
||||
|
||||
# Indicate that gfids are present.
|
||||
.def @feat.00; .scl 3; .type 0; .endef
|
||||
.globl @feat.00
|
||||
@feat.00 = 0x800
|
||||
|
||||
.def f1; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,f1
|
||||
.global f1
|
||||
f1:
|
||||
movl $42, %eax
|
||||
retq
|
||||
|
||||
.def f2; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,f2
|
||||
.global f2
|
||||
f2:
|
||||
movl $13, %eax
|
||||
retq
|
||||
|
||||
.section .data,"dw",one_only,fp1
|
||||
.globl fp1
|
||||
fp1:
|
||||
.quad f1
|
||||
|
||||
.section .data,"dw",one_only,fp2
|
||||
.globl fp2
|
||||
fp2:
|
||||
.quad f2
|
||||
|
||||
.section .gfids$y,"dr",associative,fp1
|
||||
.symidx f1
|
||||
.byte 0
|
||||
|
||||
.section .gfids$y,"dr",associative,fp2
|
||||
.symidx f2
|
||||
.long 0x400
|
||||
|
||||
.def main; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,main
|
||||
.globl main
|
||||
main:
|
||||
callq *fp1(%rip)
|
||||
callq *fp2(%rip)
|
||||
xor %eax, %eax
|
||||
retq
|
||||
|
||||
.section .rdata,"dr"
|
||||
.globl _load_config_used
|
||||
_load_config_used:
|
||||
.long 256
|
||||
.fill 124, 1, 0
|
||||
.quad __guard_fids_table
|
||||
.quad __guard_fids_count
|
||||
.long __guard_flags
|
||||
.fill 128, 1, 0
|
|
@ -0,0 +1,96 @@
|
|||
# RUN: grep -B99999 [S]PLITMARKER %s | llvm-mc -triple x86_64-windows-msvc -filetype=obj -o %t1.obj
|
||||
# RUN: grep -A99999 [S]PLITMARKER %s | llvm-mc -triple x86_64-windows-msvc -filetype=obj -o %t2.obj
|
||||
# RUN: lld-link %t1.obj %t2.obj -guard:cf -out:%t.exe -entry:main -opt:noref
|
||||
# RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s
|
||||
|
||||
# CHECK: ImageBase: 0x140000000
|
||||
# CHECK: LoadConfig [
|
||||
# CHECK: SEHandlerTable: 0x0
|
||||
# CHECK: SEHandlerCount: 0
|
||||
# CHECK: GuardCFCheckFunction: 0x0
|
||||
# CHECK: GuardCFCheckDispatch: 0x0
|
||||
# CHECK: GuardCFFunctionTable: 0x14000{{.*}}
|
||||
# CHECK: GuardCFFunctionCount: 3
|
||||
# CHECK: GuardFlags: 0x500
|
||||
# CHECK: GuardAddressTakenIatEntryTable: 0x0
|
||||
# CHECK: GuardAddressTakenIatEntryCount: 0
|
||||
# CHECK: GuardLongJumpTargetTable: 0x0
|
||||
# CHECK: GuardLongJumpTargetCount: 0
|
||||
# CHECK: ]
|
||||
# CHECK: GuardFidTable [
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: ]
|
||||
|
||||
|
||||
# Indicate that no gfids are present. All symbols used by relocations in this
|
||||
# file will be considered address-taken.
|
||||
.def @feat.00; .scl 3; .type 0; .endef
|
||||
.globl @feat.00
|
||||
@feat.00 = 0
|
||||
|
||||
.def main; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,main
|
||||
.globl main
|
||||
main:
|
||||
subq $8, %rsp
|
||||
leaq foo(%rip), %rdx
|
||||
callq bar
|
||||
movl $0, %eax
|
||||
addq $8, %rsp
|
||||
retq
|
||||
|
||||
# Should not appear in gfids table.
|
||||
.def baz; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,baz
|
||||
.globl baz
|
||||
baz:
|
||||
mov $1, %eax
|
||||
retq
|
||||
|
||||
.def qux; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,qux
|
||||
.globl qux
|
||||
qux:
|
||||
mov $2, %eax
|
||||
retq
|
||||
|
||||
.def quxx; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,quxx
|
||||
.globl quxx
|
||||
quxx:
|
||||
mov $3, %eax
|
||||
retq
|
||||
|
||||
# Load config.
|
||||
.section .rdata,"dr"
|
||||
.globl _load_config_used
|
||||
_load_config_used:
|
||||
.long 256
|
||||
.fill 124, 1, 0
|
||||
.quad __guard_fids_table
|
||||
.quad __guard_fids_count
|
||||
.long __guard_flags
|
||||
.fill 128, 1, 0
|
||||
|
||||
# SPLITMARKER
|
||||
|
||||
# Indicate that gfids are present. This file does not take any addresses.
|
||||
.def @feat.00; .scl 3; .type 0; .endef
|
||||
.globl @feat.00
|
||||
@feat.00 = 0x800
|
||||
|
||||
.def foo; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,foo
|
||||
.global foo
|
||||
foo:
|
||||
movl $42, %eax
|
||||
retq
|
||||
|
||||
.def bar; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,bar
|
||||
.global bar
|
||||
bar:
|
||||
movl $13, %eax
|
||||
retq
|
|
@ -0,0 +1,128 @@
|
|||
# RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
|
||||
# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:noref -entry:main
|
||||
# RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC
|
||||
# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:ref -entry:main
|
||||
# RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-GC
|
||||
|
||||
# This assembly is meant to mimic what CL emits for this kind of C code when
|
||||
# /Gw (-fdata-sections) is enabled:
|
||||
# int f() { return 42; }
|
||||
# int g() { return 13; }
|
||||
# int (*fp1)() = &f;
|
||||
# int (*fp2)() = &g;
|
||||
# int main() {
|
||||
# return fp1();
|
||||
# }
|
||||
# Compile with 'cl -c -guard:cf -Gw -O1' and note the two associative .gfids$y
|
||||
# sections.
|
||||
|
||||
# Expect 3 entries: main, f, and g.
|
||||
|
||||
# CHECK-NOGC: ImageBase: 0x140000000
|
||||
# CHECK-NOGC: LoadConfig [
|
||||
# CHECK-NOGC: SEHandlerTable: 0x0
|
||||
# CHECK-NOGC: SEHandlerCount: 0
|
||||
# CHECK-NOGC: GuardCFCheckFunction: 0x0
|
||||
# CHECK-NOGC: GuardCFCheckDispatch: 0x0
|
||||
# CHECK-NOGC: GuardCFFunctionTable: 0x14000{{.*}}
|
||||
# CHECK-NOGC: GuardCFFunctionCount: 3
|
||||
# CHECK-NOGC: GuardFlags: 0x500
|
||||
# CHECK-NOGC: GuardAddressTakenIatEntryTable: 0x0
|
||||
# CHECK-NOGC: GuardAddressTakenIatEntryCount: 0
|
||||
# CHECK-NOGC: GuardLongJumpTargetTable: 0x0
|
||||
# CHECK-NOGC: GuardLongJumpTargetCount: 0
|
||||
# CHECK-NOGC: ]
|
||||
# CHECK-NOGC: GuardFidTable [
|
||||
# CHECK-NOGC-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NOGC-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NOGC-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NOGC-NEXT: ]
|
||||
|
||||
# Expect 2 entries: main and f. fp2 was discarded, so g was only used as a
|
||||
# direct call target.
|
||||
|
||||
# CHECK-GC: ImageBase: 0x140000000
|
||||
# CHECK-GC: LoadConfig [
|
||||
# CHECK-GC: SEHandlerTable: 0x0
|
||||
# CHECK-GC: SEHandlerCount: 0
|
||||
# CHECK-GC: GuardCFCheckFunction: 0x0
|
||||
# CHECK-GC: GuardCFCheckDispatch: 0x0
|
||||
# CHECK-GC: GuardCFFunctionTable: 0x14000{{.*}}
|
||||
# CHECK-GC: GuardCFFunctionCount: 2
|
||||
# CHECK-GC: GuardFlags: 0x500
|
||||
# CHECK-GC: GuardAddressTakenIatEntryTable: 0x0
|
||||
# CHECK-GC: GuardAddressTakenIatEntryCount: 0
|
||||
# CHECK-GC: GuardLongJumpTargetTable: 0x0
|
||||
# CHECK-GC: GuardLongJumpTargetCount: 0
|
||||
# CHECK-GC: ]
|
||||
# CHECK-GC: GuardFidTable [
|
||||
# CHECK-GC-NEXT: 0x14000{{.*}}
|
||||
# CHECK-GC-NEXT: 0x14000{{.*}}
|
||||
# CHECK-GC-NEXT: ]
|
||||
|
||||
|
||||
# We need @feat.00 to have 0x800 to indicate .gfids are present.
|
||||
.def @feat.00;
|
||||
.scl 3;
|
||||
.type 0;
|
||||
.endef
|
||||
.globl @feat.00
|
||||
@feat.00 = 0x801
|
||||
|
||||
.def main;
|
||||
.scl 2;
|
||||
.type 32;
|
||||
.endef
|
||||
.section .text,"xr",one_only,main
|
||||
.globl main
|
||||
main:
|
||||
# Call g directly so that it is not dead stripped.
|
||||
callq g
|
||||
rex64 jmpq *fp1(%rip)
|
||||
|
||||
.def f;
|
||||
.scl 3;
|
||||
.type 32;
|
||||
.endef
|
||||
.section .text,"xr",one_only,f
|
||||
f:
|
||||
movl $42, %eax
|
||||
retq
|
||||
|
||||
.section .data,"dw",one_only,fp1
|
||||
.globl fp1
|
||||
fp1:
|
||||
.quad f
|
||||
|
||||
.section .gfids$y,"dr",associative,fp1
|
||||
.symidx f
|
||||
|
||||
# Section GC will remove the following, so 'g' should not be present in the
|
||||
# guard fid table.
|
||||
|
||||
.def g;
|
||||
.scl 3;
|
||||
.type 32;
|
||||
.endef
|
||||
.section .text,"xr",one_only,g
|
||||
g:
|
||||
movl $13, %eax
|
||||
retq
|
||||
|
||||
.section .data,"dw",one_only,fp2
|
||||
.globl fp2
|
||||
fp2:
|
||||
.quad g
|
||||
|
||||
.section .gfids$y,"dr",associative,fp2
|
||||
.symidx g
|
||||
|
||||
.section .rdata,"dr"
|
||||
.globl _load_config_used
|
||||
_load_config_used:
|
||||
.long 256
|
||||
.fill 124, 1, 0
|
||||
.quad __guard_fids_table
|
||||
.quad __guard_fids_count
|
||||
.long __guard_flags
|
||||
.fill 128, 1, 0
|
|
@ -0,0 +1,87 @@
|
|||
# RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
|
||||
# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:icf -entry:main
|
||||
# RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK
|
||||
|
||||
# This assembly is meant to mimic what CL emits for this kind of C code:
|
||||
# int icf1() { return 42; }
|
||||
# int icf2() { return 42; }
|
||||
# int (*fp1)() = &icf1;
|
||||
# int (*fp2)() = &icf2;
|
||||
# int main() {
|
||||
# return fp1();
|
||||
# return fp2();
|
||||
# }
|
||||
|
||||
# 'icf1' and 'icf2' are address taken, but should be merged into one entry.
|
||||
# There are two entries in the table because 'main' is included.
|
||||
|
||||
# CHECK: ImageBase: 0x140000000
|
||||
# CHECK: LoadConfig [
|
||||
# CHECK: SEHandlerTable: 0x0
|
||||
# CHECK: SEHandlerCount: 0
|
||||
# CHECK: GuardCFCheckFunction: 0x0
|
||||
# CHECK: GuardCFCheckDispatch: 0x0
|
||||
# CHECK: GuardCFFunctionTable: 0x14000{{.*}}
|
||||
# CHECK: GuardCFFunctionCount: 2
|
||||
# CHECK: GuardFlags: 0x500
|
||||
# CHECK: GuardAddressTakenIatEntryTable: 0x0
|
||||
# CHECK: GuardAddressTakenIatEntryCount: 0
|
||||
# CHECK: GuardLongJumpTargetTable: 0x0
|
||||
# CHECK: GuardLongJumpTargetCount: 0
|
||||
# CHECK: ]
|
||||
# CHECK: GuardFidTable [
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: 0x14000{{.*}}
|
||||
# CHECK-NEXT: ]
|
||||
|
||||
|
||||
# Indicate that gfids are present.
|
||||
.def @feat.00; .scl 3; .type 0; .endef
|
||||
.globl @feat.00
|
||||
@feat.00 = 0x800
|
||||
|
||||
.def icf1; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,icf1
|
||||
.global icf1
|
||||
icf1:
|
||||
movl $42, %eax
|
||||
retq
|
||||
|
||||
.def icf2; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,icf2
|
||||
.global icf2
|
||||
icf2:
|
||||
movl $42, %eax
|
||||
retq
|
||||
|
||||
# Take their two addresses.
|
||||
.data
|
||||
.globl fp1
|
||||
fp1:
|
||||
.quad icf1
|
||||
.globl fp2
|
||||
fp2:
|
||||
.quad icf2
|
||||
|
||||
.section .gfids$y,"dr"
|
||||
.symidx icf1
|
||||
.symidx icf2
|
||||
|
||||
.def main; .scl 2; .type 32; .endef
|
||||
.section .text,"xr",one_only,main
|
||||
.globl main
|
||||
main:
|
||||
callq *fp1(%rip)
|
||||
callq *fp2(%rip)
|
||||
xor %eax, %eax
|
||||
retq
|
||||
|
||||
.section .rdata,"dr"
|
||||
.globl _load_config_used
|
||||
_load_config_used:
|
||||
.long 256
|
||||
.fill 124, 1, 0
|
||||
.quad __guard_fids_table
|
||||
.quad __guard_fids_count
|
||||
.long __guard_flags
|
||||
.fill 128, 1, 0
|
Loading…
Reference in New Issue