[lld] a2923b2 - Implement CET Shadow Stack (Intel Controlflow Enforcement Technology) support on Windows

Rui Ueyama via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 16 02:03:00 PDT 2020


Author: Rui Ueyama
Date: 2020-03-16T17:51:32+09:00
New Revision: a2923b2a1e4536dd14e93bed5c1306e0e54d5304

URL: https://github.com/llvm/llvm-project/commit/a2923b2a1e4536dd14e93bed5c1306e0e54d5304
DIFF: https://github.com/llvm/llvm-project/commit/a2923b2a1e4536dd14e93bed5c1306e0e54d5304.diff

LOG: Implement CET Shadow Stack (Intel Controlflow Enforcement Technology) support on Windows

Patch by Petr Penzin.

Windows support for CET is limited to shadow stack, which is enabled
by setting a PE bit in the linker.

Docs:

MSVC linker flag:
https://docs.microsoft.com/en-us/cpp/build/reference/cetcompat?view=vs-2019

IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT PE bit:
https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#extended-dll-characteristics

Differential Revision: https://reviews.llvm.org/D70606

Added: 
    llvm/test/tools/llvm-readobj/COFF/Inputs/has-cet.exe
    llvm/test/tools/llvm-readobj/COFF/cetcompat.test

Modified: 
    lld/COFF/Config.h
    lld/COFF/Driver.cpp
    lld/COFF/Options.td
    lld/COFF/Writer.cpp
    lld/test/COFF/options.test
    llvm/include/llvm/BinaryFormat/COFF.h
    llvm/tools/llvm-readobj/COFFDumper.cpp

Removed: 
    


################################################################################
diff  --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 2690ea5c4082..af86b6992272 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -211,6 +211,7 @@ struct Configuration {
   uint32_t functionPadMin = 0;
   bool dynamicBase = true;
   bool allowBind = true;
+  bool cetCompat = false;
   bool nxCompat = true;
   bool allowIsolation = true;
   bool terminalServerAware = true;

diff  --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index a85dfda2dea1..5320b8b83ce0 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1549,6 +1549,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
                        !args.hasArg(OPT_profile));
   config->integrityCheck =
       args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
+  config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
   config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
   for (auto *arg : args.filtered(OPT_swaprun))
     parseSwaprun(arg->getValue());

diff  --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 328a120cb711..cea02e7a0042 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -148,6 +148,8 @@ defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
 defm appcontainer : B<"appcontainer",
                       "Image can only be run in an app container",
                       "Image can run outside an app container (default)">;
+defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
+                   "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
 defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
                      "Disable ASLR (default when /fixed)">;
 defm fixed : B<"fixed", "Disable base relocations",

diff  --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index fbef82af9d36..bb583757cb61 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -91,7 +91,8 @@ namespace {
 
 class DebugDirectoryChunk : public NonSectionChunk {
 public:
-  DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
+  DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
+                      bool writeRepro)
       : records(r), writeRepro(writeRepro) {}
 
   size_t getSize() const override {
@@ -101,11 +102,11 @@ class DebugDirectoryChunk : public NonSectionChunk {
   void writeTo(uint8_t *b) const override {
     auto *d = reinterpret_cast<debug_directory *>(b);
 
-    for (const Chunk *record : records) {
-      OutputSection *os = record->getOutputSection();
-      uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA());
-      fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(),
-                record->getRVA(), offs);
+    for (const std::pair<COFF::DebugType, Chunk *> record : records) {
+      Chunk *c = record.second;
+      OutputSection *os = c->getOutputSection();
+      uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
+      fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
       ++d;
     }
 
@@ -140,7 +141,7 @@ class DebugDirectoryChunk : public NonSectionChunk {
   }
 
   mutable std::vector<support::ulittle32_t *> timeDateStamps;
-  const std::vector<Chunk *> &records;
+  const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
   bool writeRepro;
 };
 
@@ -165,6 +166,17 @@ class CVDebugRecordChunk : public NonSectionChunk {
   mutable codeview::DebugInfo *buildId = nullptr;
 };
 
+class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
+public:
+  ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}
+
+  size_t getSize() const override { return 4; }
+
+  void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }
+
+  uint32_t characteristics = 0;
+};
+
 // PartialSection represents a group of chunks that contribute to an
 // OutputSection. Collating a collection of PartialSections of same name and
 // characteristics constitutes the OutputSection.
@@ -250,7 +262,7 @@ class Writer {
   bool setNoSEHCharacteristic = false;
 
   DebugDirectoryChunk *debugDirectory = nullptr;
-  std::vector<Chunk *> debugRecords;
+  std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
   CVDebugRecordChunk *buildId = nullptr;
   ArrayRef<uint8_t> sectionTable;
 
@@ -920,8 +932,9 @@ void Writer::createMiscChunks() {
 
   // Create Debug Information Chunks
   OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
-  if (config->debug || config->repro) {
+  if (config->debug || config->repro || config->cetCompat) {
     debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
+    debugDirectory->setAlignment(4);
     debugInfoSec->addChunk(debugDirectory);
   }
 
@@ -931,10 +944,20 @@ void Writer::createMiscChunks() {
     // allowing a debugger to match a PDB and an executable.  So we need it even
     // if we're ultimately not going to write CodeView data to the PDB.
     buildId = make<CVDebugRecordChunk>();
-    debugRecords.push_back(buildId);
+    debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
+  }
+
+  if (config->cetCompat) {
+    ExtendedDllCharacteristicsChunk *extendedDllChars =
+        make<ExtendedDllCharacteristicsChunk>(
+            IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT);
+    debugRecords.push_back(
+        {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars});
+  }
 
-    for (Chunk *c : debugRecords)
-      debugInfoSec->addChunk(c);
+  if (debugRecords.size() > 0) {
+    for (std::pair<COFF::DebugType, Chunk *> r : debugRecords)
+      debugInfoSec->addChunk(r.second);
   }
 
   // Create SEH table. x86-only.

diff  --git a/lld/test/COFF/options.test b/lld/test/COFF/options.test
index 37220f93de18..b700c6529f9f 100644
--- a/lld/test/COFF/options.test
+++ b/lld/test/COFF/options.test
@@ -50,6 +50,16 @@ NXCOMPAT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
 # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=NONXCOMPAT %s
 NONXCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
 
+# RUN: lld-link /out:%t.exe /entry:main /cetcompat %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CETCOMPAT %s
+CETCOMPAT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT
+
+# RUN: lld-link /out:%t.exe /entry:main %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s
+# RUN: lld-link /out:%t.exe /entry:main /cetcompat:no %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s
+NONCETCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT
+
 # RUN: lld-link /out:%t.exe /entry:main /swaprun:CD %t.obj
 # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=SWAPCD %s
 # RUN: lld-link /out:%t.exe /entry:main /swaprun:cd,net %t.obj

diff  --git a/llvm/include/llvm/BinaryFormat/COFF.h b/llvm/include/llvm/BinaryFormat/COFF.h
index 626e0a431e93..1919d7f0dece 100644
--- a/llvm/include/llvm/BinaryFormat/COFF.h
+++ b/llvm/include/llvm/BinaryFormat/COFF.h
@@ -642,6 +642,11 @@ enum DLLCharacteristics : unsigned {
   IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
 };
 
+enum ExtendedDLLCharacteristics : unsigned {
+  /// Image is CET compatible
+  IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT = 0x0001
+};
+
 enum DebugType : unsigned {
   IMAGE_DEBUG_TYPE_UNKNOWN = 0,
   IMAGE_DEBUG_TYPE_COFF = 1,
@@ -660,6 +665,7 @@ enum DebugType : unsigned {
   IMAGE_DEBUG_TYPE_ILTCG = 14,
   IMAGE_DEBUG_TYPE_MPX = 15,
   IMAGE_DEBUG_TYPE_REPRO = 16,
+  IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20,
 };
 
 enum BaseRelocationType : unsigned {

diff  --git a/llvm/test/tools/llvm-readobj/COFF/Inputs/has-cet.exe b/llvm/test/tools/llvm-readobj/COFF/Inputs/has-cet.exe
new file mode 100644
index 000000000000..c77060d976d2
Binary files /dev/null and b/llvm/test/tools/llvm-readobj/COFF/Inputs/has-cet.exe 
diff er

diff  --git a/llvm/test/tools/llvm-readobj/COFF/cetcompat.test b/llvm/test/tools/llvm-readobj/COFF/cetcompat.test
new file mode 100644
index 000000000000..a973b5c0a208
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/cetcompat.test
@@ -0,0 +1,16 @@
+# To regenerate has-cet.exe
+# $ echo int main() { return 0; } > has-cet.c
+# $ cl has-cet.c /link /cetcompat
+RUN: llvm-readobj --coff-debug-directory %p/Inputs/has-cet.exe | FileCheck %s
+
+CHECK:  DebugEntry {
+CHECK:    Characteristics: 0x0
+CHECK:    Type: ExtendedDLLCharacteristics (0x14)
+CHECK:    ExtendedCharacteristics [ (0x1)
+CHECK:      IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1)
+CHECK:    ]
+CHECK:    RawData (
+CHECK:      0000: 01000000                             |....|
+CHECK:    )
+CHECK:  }
+

diff  --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index ffe331a3d240..bd4ab0e7dec9 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -104,6 +104,7 @@ class COFFDumper : public ObjDumper {
                           bool GHash) override;
   void printStackMap() const override;
   void printAddrsig() override;
+
 private:
   void printSymbols() override;
   void printDynamicSymbols() override;
@@ -409,6 +410,11 @@ static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = {
   LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE),
 };
 
+static const EnumEntry<COFF::ExtendedDLLCharacteristics>
+    PEExtendedDLLCharacteristics[] = {
+        LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT),
+};
+
 static const EnumEntry<COFF::SectionCharacteristics>
 ImageSectionCharacteristics[] = {
   LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD           ),
@@ -516,23 +522,25 @@ static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = {
 };
 
 static const EnumEntry<COFF::DebugType> ImageDebugType[] = {
-  { "Unknown"    , COFF::IMAGE_DEBUG_TYPE_UNKNOWN       },
-  { "COFF"       , COFF::IMAGE_DEBUG_TYPE_COFF          },
-  { "CodeView"   , COFF::IMAGE_DEBUG_TYPE_CODEVIEW      },
-  { "FPO"        , COFF::IMAGE_DEBUG_TYPE_FPO           },
-  { "Misc"       , COFF::IMAGE_DEBUG_TYPE_MISC          },
-  { "Exception"  , COFF::IMAGE_DEBUG_TYPE_EXCEPTION     },
-  { "Fixup"      , COFF::IMAGE_DEBUG_TYPE_FIXUP         },
-  { "OmapToSrc"  , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC   },
-  { "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC },
-  { "Borland"    , COFF::IMAGE_DEBUG_TYPE_BORLAND       },
-  { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10    },
-  { "CLSID"      , COFF::IMAGE_DEBUG_TYPE_CLSID         },
-  { "VCFeature"  , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE    },
-  { "POGO"       , COFF::IMAGE_DEBUG_TYPE_POGO          },
-  { "ILTCG"      , COFF::IMAGE_DEBUG_TYPE_ILTCG         },
-  { "MPX"        , COFF::IMAGE_DEBUG_TYPE_MPX           },
-  { "Repro"      , COFF::IMAGE_DEBUG_TYPE_REPRO         },
+    {"Unknown", COFF::IMAGE_DEBUG_TYPE_UNKNOWN},
+    {"COFF", COFF::IMAGE_DEBUG_TYPE_COFF},
+    {"CodeView", COFF::IMAGE_DEBUG_TYPE_CODEVIEW},
+    {"FPO", COFF::IMAGE_DEBUG_TYPE_FPO},
+    {"Misc", COFF::IMAGE_DEBUG_TYPE_MISC},
+    {"Exception", COFF::IMAGE_DEBUG_TYPE_EXCEPTION},
+    {"Fixup", COFF::IMAGE_DEBUG_TYPE_FIXUP},
+    {"OmapToSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC},
+    {"OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC},
+    {"Borland", COFF::IMAGE_DEBUG_TYPE_BORLAND},
+    {"Reserved10", COFF::IMAGE_DEBUG_TYPE_RESERVED10},
+    {"CLSID", COFF::IMAGE_DEBUG_TYPE_CLSID},
+    {"VCFeature", COFF::IMAGE_DEBUG_TYPE_VC_FEATURE},
+    {"POGO", COFF::IMAGE_DEBUG_TYPE_POGO},
+    {"ILTCG", COFF::IMAGE_DEBUG_TYPE_ILTCG},
+    {"MPX", COFF::IMAGE_DEBUG_TYPE_MPX},
+    {"Repro", COFF::IMAGE_DEBUG_TYPE_REPRO},
+    {"ExtendedDLLCharacteristics",
+     COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS},
 };
 
 static const EnumEntry<COFF::WeakExternalCharacteristics>
@@ -736,12 +744,19 @@ void COFFDumper::printCOFFDebugDirectory() {
         W.printString("PDBFileName", PDBFileName);
       }
     } else if (D.SizeOfData != 0) {
-      // FIXME: Type values of 12 and 13 are commonly observed but are not in
-      // the documented type enum.  Figure out what they mean.
+      // FIXME: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and
+      // IMAGE_DEBUG_TYPE_POGO?
       ArrayRef<uint8_t> RawData;
       if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData,
                                                          D.SizeOfData, RawData))
         reportError(errorCodeToError(EC), Obj->getFileName());
+      if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
+        // FIXME right now the only possible value would fit in 8 bits,
+        // but that might change in the future
+        uint16_t Characteristics = RawData[0];
+        W.printFlags("ExtendedCharacteristics", Characteristics,
+                     makeArrayRef(PEExtendedDLLCharacteristics));
+      }
       W.printBinaryBlock("RawData", RawData);
     }
   }


        


More information about the llvm-commits mailing list