[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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 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