Skip to content

Commit

Permalink
[COFF] Add minimal support for /guard:cf
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rnk committed Feb 6, 2018
1 parent dc51fb4 commit af2f7da
Show file tree
Hide file tree
Showing 14 changed files with 632 additions and 53 deletions.
8 changes: 5 additions & 3 deletions lld/COFF/Chunks.cpp
Expand Up @@ -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.
Expand Down
42 changes: 36 additions & 6 deletions lld/COFF/Chunks.h
Expand Up @@ -320,17 +320,41 @@ class LocalImportChunk : public Chunk {
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.
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions lld/COFF/Config.h
Expand Up @@ -112,6 +112,9 @@ struct Configuration {

bool SaveTemps = false;

// /guard:cf
bool GuardCF;

// Used for SafeSEH.
Symbol *SEHTable = nullptr;
Symbol *SEHCount = nullptr;
Expand Down
10 changes: 6 additions & 4 deletions lld/COFF/Driver.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions lld/COFF/Driver.h
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions lld/COFF/DriverUtils.cpp
Expand Up @@ -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) {
Expand Down
18 changes: 6 additions & 12 deletions lld/COFF/InputFiles.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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())
Expand Down
26 changes: 20 additions & 6 deletions lld/COFF/InputFiles.h
Expand Up @@ -110,6 +110,8 @@ class ObjFile : public InputFile {
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
Expand All @@ -123,13 +125,17 @@ class ObjFile : public InputFile {

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
Expand Down Expand Up @@ -165,6 +171,14 @@ class ObjFile : public InputFile {
// 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.
Expand Down
1 change: 1 addition & 0 deletions lld/COFF/Options.td
Expand Up @@ -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">;
Expand Down

0 comments on commit af2f7da

Please sign in to comment.