Skip to content

Commit a2923b2

Browse files
committed
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
1 parent 2005c60 commit a2923b2

File tree

9 files changed

+105
-31
lines changed

9 files changed

+105
-31
lines changed

lld/COFF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ struct Configuration {
211211
uint32_t functionPadMin = 0;
212212
bool dynamicBase = true;
213213
bool allowBind = true;
214+
bool cetCompat = false;
214215
bool nxCompat = true;
215216
bool allowIsolation = true;
216217
bool terminalServerAware = true;

lld/COFF/Driver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
15491549
!args.hasArg(OPT_profile));
15501550
config->integrityCheck =
15511551
args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
1552+
config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
15521553
config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
15531554
for (auto *arg : args.filtered(OPT_swaprun))
15541555
parseSwaprun(arg->getValue());

lld/COFF/Options.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
148148
defm appcontainer : B<"appcontainer",
149149
"Image can only be run in an app container",
150150
"Image can run outside an app container (default)">;
151+
defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
152+
"Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
151153
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
152154
"Disable ASLR (default when /fixed)">;
153155
defm fixed : B<"fixed", "Disable base relocations",

lld/COFF/Writer.cpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ namespace {
9191

9292
class DebugDirectoryChunk : public NonSectionChunk {
9393
public:
94-
DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
94+
DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
95+
bool writeRepro)
9596
: records(r), writeRepro(writeRepro) {}
9697

9798
size_t getSize() const override {
@@ -101,11 +102,11 @@ class DebugDirectoryChunk : public NonSectionChunk {
101102
void writeTo(uint8_t *b) const override {
102103
auto *d = reinterpret_cast<debug_directory *>(b);
103104

104-
for (const Chunk *record : records) {
105-
OutputSection *os = record->getOutputSection();
106-
uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA());
107-
fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(),
108-
record->getRVA(), offs);
105+
for (const std::pair<COFF::DebugType, Chunk *> record : records) {
106+
Chunk *c = record.second;
107+
OutputSection *os = c->getOutputSection();
108+
uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
109+
fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
109110
++d;
110111
}
111112

@@ -140,7 +141,7 @@ class DebugDirectoryChunk : public NonSectionChunk {
140141
}
141142

142143
mutable std::vector<support::ulittle32_t *> timeDateStamps;
143-
const std::vector<Chunk *> &records;
144+
const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
144145
bool writeRepro;
145146
};
146147

@@ -165,6 +166,17 @@ class CVDebugRecordChunk : public NonSectionChunk {
165166
mutable codeview::DebugInfo *buildId = nullptr;
166167
};
167168

169+
class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
170+
public:
171+
ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}
172+
173+
size_t getSize() const override { return 4; }
174+
175+
void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }
176+
177+
uint32_t characteristics = 0;
178+
};
179+
168180
// PartialSection represents a group of chunks that contribute to an
169181
// OutputSection. Collating a collection of PartialSections of same name and
170182
// characteristics constitutes the OutputSection.
@@ -250,7 +262,7 @@ class Writer {
250262
bool setNoSEHCharacteristic = false;
251263

252264
DebugDirectoryChunk *debugDirectory = nullptr;
253-
std::vector<Chunk *> debugRecords;
265+
std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
254266
CVDebugRecordChunk *buildId = nullptr;
255267
ArrayRef<uint8_t> sectionTable;
256268

@@ -920,8 +932,9 @@ void Writer::createMiscChunks() {
920932

921933
// Create Debug Information Chunks
922934
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
923-
if (config->debug || config->repro) {
935+
if (config->debug || config->repro || config->cetCompat) {
924936
debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
937+
debugDirectory->setAlignment(4);
925938
debugInfoSec->addChunk(debugDirectory);
926939
}
927940

@@ -931,10 +944,20 @@ void Writer::createMiscChunks() {
931944
// allowing a debugger to match a PDB and an executable. So we need it even
932945
// if we're ultimately not going to write CodeView data to the PDB.
933946
buildId = make<CVDebugRecordChunk>();
934-
debugRecords.push_back(buildId);
947+
debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
948+
}
949+
950+
if (config->cetCompat) {
951+
ExtendedDllCharacteristicsChunk *extendedDllChars =
952+
make<ExtendedDllCharacteristicsChunk>(
953+
IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT);
954+
debugRecords.push_back(
955+
{COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars});
956+
}
935957

936-
for (Chunk *c : debugRecords)
937-
debugInfoSec->addChunk(c);
958+
if (debugRecords.size() > 0) {
959+
for (std::pair<COFF::DebugType, Chunk *> r : debugRecords)
960+
debugInfoSec->addChunk(r.second);
938961
}
939962

940963
// Create SEH table. x86-only.

lld/test/COFF/options.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ NXCOMPAT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
5050
# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=NONXCOMPAT %s
5151
NONXCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
5252

53+
# RUN: lld-link /out:%t.exe /entry:main /cetcompat %t.obj
54+
# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CETCOMPAT %s
55+
CETCOMPAT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT
56+
57+
# RUN: lld-link /out:%t.exe /entry:main %t.obj
58+
# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s
59+
# RUN: lld-link /out:%t.exe /entry:main /cetcompat:no %t.obj
60+
# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s
61+
NONCETCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT
62+
5363
# RUN: lld-link /out:%t.exe /entry:main /swaprun:CD %t.obj
5464
# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=SWAPCD %s
5565
# RUN: lld-link /out:%t.exe /entry:main /swaprun:cd,net %t.obj

llvm/include/llvm/BinaryFormat/COFF.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,11 @@ enum DLLCharacteristics : unsigned {
642642
IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
643643
};
644644

645+
enum ExtendedDLLCharacteristics : unsigned {
646+
/// Image is CET compatible
647+
IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT = 0x0001
648+
};
649+
645650
enum DebugType : unsigned {
646651
IMAGE_DEBUG_TYPE_UNKNOWN = 0,
647652
IMAGE_DEBUG_TYPE_COFF = 1,
@@ -660,6 +665,7 @@ enum DebugType : unsigned {
660665
IMAGE_DEBUG_TYPE_ILTCG = 14,
661666
IMAGE_DEBUG_TYPE_MPX = 15,
662667
IMAGE_DEBUG_TYPE_REPRO = 16,
668+
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20,
663669
};
664670

665671
enum BaseRelocationType : unsigned {
Binary file not shown.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# To regenerate has-cet.exe
2+
# $ echo int main() { return 0; } > has-cet.c
3+
# $ cl has-cet.c /link /cetcompat
4+
RUN: llvm-readobj --coff-debug-directory %p/Inputs/has-cet.exe | FileCheck %s
5+
6+
CHECK: DebugEntry {
7+
CHECK: Characteristics: 0x0
8+
CHECK: Type: ExtendedDLLCharacteristics (0x14)
9+
CHECK: ExtendedCharacteristics [ (0x1)
10+
CHECK: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1)
11+
CHECK: ]
12+
CHECK: RawData (
13+
CHECK: 0000: 01000000 |....|
14+
CHECK: )
15+
CHECK: }
16+

llvm/tools/llvm-readobj/COFFDumper.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class COFFDumper : public ObjDumper {
104104
bool GHash) override;
105105
void printStackMap() const override;
106106
void printAddrsig() override;
107+
107108
private:
108109
void printSymbols() override;
109110
void printDynamicSymbols() override;
@@ -409,6 +410,11 @@ static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = {
409410
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE),
410411
};
411412

413+
static const EnumEntry<COFF::ExtendedDLLCharacteristics>
414+
PEExtendedDLLCharacteristics[] = {
415+
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT),
416+
};
417+
412418
static const EnumEntry<COFF::SectionCharacteristics>
413419
ImageSectionCharacteristics[] = {
414420
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ),
@@ -516,23 +522,25 @@ static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = {
516522
};
517523

518524
static const EnumEntry<COFF::DebugType> ImageDebugType[] = {
519-
{ "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN },
520-
{ "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF },
521-
{ "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW },
522-
{ "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO },
523-
{ "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC },
524-
{ "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION },
525-
{ "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP },
526-
{ "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC },
527-
{ "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC },
528-
{ "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND },
529-
{ "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 },
530-
{ "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID },
531-
{ "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE },
532-
{ "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO },
533-
{ "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG },
534-
{ "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX },
535-
{ "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO },
525+
{"Unknown", COFF::IMAGE_DEBUG_TYPE_UNKNOWN},
526+
{"COFF", COFF::IMAGE_DEBUG_TYPE_COFF},
527+
{"CodeView", COFF::IMAGE_DEBUG_TYPE_CODEVIEW},
528+
{"FPO", COFF::IMAGE_DEBUG_TYPE_FPO},
529+
{"Misc", COFF::IMAGE_DEBUG_TYPE_MISC},
530+
{"Exception", COFF::IMAGE_DEBUG_TYPE_EXCEPTION},
531+
{"Fixup", COFF::IMAGE_DEBUG_TYPE_FIXUP},
532+
{"OmapToSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC},
533+
{"OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC},
534+
{"Borland", COFF::IMAGE_DEBUG_TYPE_BORLAND},
535+
{"Reserved10", COFF::IMAGE_DEBUG_TYPE_RESERVED10},
536+
{"CLSID", COFF::IMAGE_DEBUG_TYPE_CLSID},
537+
{"VCFeature", COFF::IMAGE_DEBUG_TYPE_VC_FEATURE},
538+
{"POGO", COFF::IMAGE_DEBUG_TYPE_POGO},
539+
{"ILTCG", COFF::IMAGE_DEBUG_TYPE_ILTCG},
540+
{"MPX", COFF::IMAGE_DEBUG_TYPE_MPX},
541+
{"Repro", COFF::IMAGE_DEBUG_TYPE_REPRO},
542+
{"ExtendedDLLCharacteristics",
543+
COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS},
536544
};
537545

538546
static const EnumEntry<COFF::WeakExternalCharacteristics>
@@ -736,12 +744,19 @@ void COFFDumper::printCOFFDebugDirectory() {
736744
W.printString("PDBFileName", PDBFileName);
737745
}
738746
} else if (D.SizeOfData != 0) {
739-
// FIXME: Type values of 12 and 13 are commonly observed but are not in
740-
// the documented type enum. Figure out what they mean.
747+
// FIXME: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and
748+
// IMAGE_DEBUG_TYPE_POGO?
741749
ArrayRef<uint8_t> RawData;
742750
if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData,
743751
D.SizeOfData, RawData))
744752
reportError(errorCodeToError(EC), Obj->getFileName());
753+
if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
754+
// FIXME right now the only possible value would fit in 8 bits,
755+
// but that might change in the future
756+
uint16_t Characteristics = RawData[0];
757+
W.printFlags("ExtendedCharacteristics", Characteristics,
758+
makeArrayRef(PEExtendedDLLCharacteristics));
759+
}
745760
W.printBinaryBlock("RawData", RawData);
746761
}
747762
}

0 commit comments

Comments
 (0)