[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