[lld] r324306 - [COFF] Add minimal support for /guard:cf

Reid Kleckner via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 17:58:27 PST 2018


Author: rnk
Date: Mon Feb  5 17:58:26 2018
New Revision: 324306

URL: http://llvm.org/viewvc/llvm-project?rev=324306&view=rev
Log:
[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

Added:
    lld/trunk/test/COFF/gfids-corrupt.s
    lld/trunk/test/COFF/gfids-fallback.s
    lld/trunk/test/COFF/gfids-gc.s
    lld/trunk/test/COFF/gfids-icf.s
Modified:
    lld/trunk/COFF/Chunks.cpp
    lld/trunk/COFF/Chunks.h
    lld/trunk/COFF/Config.h
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/Driver.h
    lld/trunk/COFF/DriverUtils.cpp
    lld/trunk/COFF/InputFiles.cpp
    lld/trunk/COFF/InputFiles.h
    lld/trunk/COFF/Options.td
    lld/trunk/COFF/Writer.cpp

Modified: lld/trunk/COFF/Chunks.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Chunks.cpp?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Chunks.cpp (original)
+++ lld/trunk/COFF/Chunks.cpp Mon Feb  5 17:58:26 2018
@@ -453,12 +453,14 @@ void LocalImportChunk::writeTo(uint8_t *
   }
 }
 
-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.

Modified: lld/trunk/COFF/Chunks.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Chunks.h?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Chunks.h (original)
+++ lld/trunk/COFF/Chunks.h Mon Feb  5 17:58:26 2018
@@ -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_
 } // namespace coff
 } // namespace lld
 
+namespace llvm {
+template <>
+struct DenseMapInfo<lld::coff::ChunkAndOffset>
+    : lld::coff::ChunkAndOffset::DenseMapInfo {};
+}
+
 #endif

Modified: lld/trunk/COFF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Config.h?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Config.h (original)
+++ lld/trunk/COFF/Config.h Mon Feb  5 17:58:26 2018
@@ -112,6 +112,9 @@ struct Configuration {
 
   bool SaveTemps = false;
 
+  // /guard:cf
+  bool GuardCF;
+
   // Used for SafeSEH.
   Symbol *SEHTable = nullptr;
   Symbol *SEHCount = nullptr;

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Mon Feb  5 17:58:26 2018
@@ -983,6 +983,10 @@ void LinkerDriver::link(ArrayRef<const c
   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 c
     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 c
   // 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;

Modified: lld/trunk/COFF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.h?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.h (original)
+++ lld/trunk/COFF/Driver.h Mon Feb  5 17:58:26 2018
@@ -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);

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Mon Feb  5 17:58:26 2018
@@ -128,6 +128,15 @@ void parseVersion(StringRef Arg, uint32_
     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) {

Modified: lld/trunk/COFF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.cpp?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.cpp (original)
+++ lld/trunk/COFF/InputFiles.cpp Mon Feb  5 17:58:26 2018
@@ -151,15 +151,7 @@ SectionChunk *ObjFile::readSection(uint3
   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(uint3
   // 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::createDefine
     // 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())

Modified: lld/trunk/COFF/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.h?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.h (original)
+++ lld/trunk/COFF/InputFiles.h Mon Feb  5 17:58:26 2018
@@ -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;
-
-  // The symbol table indexes of the safe exception handlers.
-  // COFF-specific and x86-only.
-  ArrayRef<llvm::support::ulittle32_t> SXData;
+  // 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;
+
+  // 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.

Modified: lld/trunk/COFF/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Options.td?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Options.td (original)
+++ lld/trunk/COFF/Options.td Mon Feb  5 17:58:26 2018
@@ -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">;

Modified: lld/trunk/COFF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.cpp?rev=324306&r1=324305&r2=324306&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.cpp (original)
+++ lld/trunk/COFF/Writer.cpp Mon Feb  5 17:58:26 2018
@@ -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 Writ
     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;
+  SymbolRVASet Handlers;
+  for (ObjFile *File : ObjFile::Instances) {
+    // FIXME: We should error here instead of earlier unless /safeseh:no was
+    // passed.
+    if (!File->hasSafeSEH())
+      return;
 
-  std::set<Defined *> Handlers;
+    markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers);
+  }
+
+  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 (!File->SEHCompat)
-      return;
-    for (uint32_t I : File->SXData)
-      if (Symbol *B = File->getSymbol(I))
-        if (B->isLive())
-          Handlers.insert(cast<Defined>(B));
+    // 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));
+      }
+    }
   }
+}
 
-  if (Handlers.empty())
+// 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

Added: lld/trunk/test/COFF/gfids-corrupt.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-corrupt.s?rev=324306&view=auto
==============================================================================
--- lld/trunk/test/COFF/gfids-corrupt.s (added)
+++ lld/trunk/test/COFF/gfids-corrupt.s Mon Feb  5 17:58:26 2018
@@ -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
+ at 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

Added: lld/trunk/test/COFF/gfids-fallback.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-fallback.s?rev=324306&view=auto
==============================================================================
--- lld/trunk/test/COFF/gfids-fallback.s (added)
+++ lld/trunk/test/COFF/gfids-fallback.s Mon Feb  5 17:58:26 2018
@@ -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
+ at 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
+ at 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

Added: lld/trunk/test/COFF/gfids-gc.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-gc.s?rev=324306&view=auto
==============================================================================
--- lld/trunk/test/COFF/gfids-gc.s (added)
+++ lld/trunk/test/COFF/gfids-gc.s Mon Feb  5 17:58:26 2018
@@ -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
+ at 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

Added: lld/trunk/test/COFF/gfids-icf.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-icf.s?rev=324306&view=auto
==============================================================================
--- lld/trunk/test/COFF/gfids-icf.s (added)
+++ lld/trunk/test/COFF/gfids-icf.s Mon Feb  5 17:58:26 2018
@@ -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
+ at 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




More information about the llvm-commits mailing list