Skip to content

Commit 1c0ca8a

Browse files
authored
[LLD][COFF] Add more variety of CET and hotpatch flags (#150761)
Those are all MS link.exe compatible flags. ### CET (Control-flow Enforcement Technology) family - Added LLD test that covers `/cetcompat[:no]` - Added `/cetcompatstrict[:no]` flag in LLD/COFF - Added `/cetipvalidationrelaxed[:no]` flag in LLD/COFF - Added `/cetdynamicapisinproc[:no]` flag in LLD/COFF ### Misc - Added `/hotpatchcompatible[:no]` flag in LLD/COFF - This flag requires at least 6 bytes of function padding (`/functionpadmin:#`) as per link.exe
1 parent 7eb889a commit 1c0ca8a

File tree

6 files changed

+191
-15
lines changed

6 files changed

+191
-15
lines changed

lld/COFF/Config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ struct Configuration {
312312
bool dynamicBase = true;
313313
bool allowBind = true;
314314
bool cetCompat = false;
315+
bool cetCompatStrict = false;
316+
bool cetCompatIpValidationRelaxed = false;
317+
bool cetCompatDynamicApisInProcOnly = false;
318+
bool hotpatchCompat = false;
315319
bool nxCompat = true;
316320
bool allowIsolation = true;
317321
bool terminalServerAware = true;

lld/COFF/Driver.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,6 +2143,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
21432143
config->integrityCheck =
21442144
args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
21452145
config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
2146+
config->cetCompatStrict =
2147+
args.hasFlag(OPT_cetcompatstrict, OPT_cetcompatstrict_no, false);
2148+
config->cetCompatIpValidationRelaxed = args.hasFlag(
2149+
OPT_cetipvalidationrelaxed, OPT_cetipvalidationrelaxed_no, false);
2150+
config->cetCompatDynamicApisInProcOnly = args.hasFlag(
2151+
OPT_cetdynamicapisinproc, OPT_cetdynamicapisinproc_no, false);
2152+
config->hotpatchCompat =
2153+
args.hasFlag(OPT_hotpatchcompatible, OPT_hotpatchcompatible_no, false);
21462154
config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
21472155
for (auto *arg : args.filtered(OPT_swaprun))
21482156
parseSwaprun(arg->getValue());
@@ -2296,6 +2304,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
22962304
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
22972305
parseFunctionPadMin(arg);
22982306

2307+
// MS link.exe compatibility, at least 6 bytes of function padding is
2308+
// required if hotpatchable
2309+
if (config->hotpatchCompat && config->functionPadMin < 6)
2310+
Err(ctx)
2311+
<< "/hotpatchcompatible: requires at least 6 bytes of /functionpadmin";
2312+
22992313
// Handle /dependentloadflag
23002314
for (auto *arg :
23012315
args.filtered(OPT_dependentloadflag, OPT_dependentloadflag_opt))

lld/COFF/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,21 @@ defm appcontainer : B<"appcontainer",
200200
"Image can run outside an app container (default)">;
201201
defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
202202
"Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
203+
defm cetcompatstrict : B<"cetcompatstrict", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack in strict mode",
204+
"Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack in strict mode (default)">;
205+
defm cetdynamicapisinproc : B<"cetdynamicapisinproc", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack in such a way that dynamic APIs allowed in process",
206+
"Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack with dynamic APIs allowed in process (default)">;
207+
defm cetipvalidationrelaxed : B<"cetipvalidationrelaxed", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack with relaxed context IP validation",
208+
"Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack with relaxed context IP validation (default)">;
203209
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
204210
"Disable ASLR (default when /fixed)">;
205211
defm fixed : B<"fixed", "Disable base relocations",
206212
"Enable base relocations (default)">;
207213
defm highentropyva : B<"highentropyva",
208214
"Enable 64-bit ASLR (default on 64-bit)",
209215
"Disable 64-bit ASLR">;
216+
defm hotpatchcompatible : B<"hotpatchcompatible", "Mark executable image as compatible with hotpatch",
217+
"Don't mark executable image as compatible with hotpatch (default)">;
210218
defm incremental : B<"incremental",
211219
"Keep original import library if contents are unchanged",
212220
"Overwrite import library even if contents are unchanged">;

lld/COFF/Writer.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,9 @@ void Writer::createMiscChunks() {
12241224
// Create Debug Information Chunks
12251225
debugInfoSec = config->mingw ? buildidSec : rdataSec;
12261226
if (config->buildIDHash != BuildIDHash::None || config->debug ||
1227-
config->repro || config->cetCompat) {
1227+
config->repro || config->cetCompat || config->cetCompatStrict ||
1228+
config->cetCompatIpValidationRelaxed ||
1229+
config->cetCompatDynamicApisInProcOnly || config->hotpatchCompat) {
12281230
debugDirectory =
12291231
make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
12301232
debugDirectory->setAlignment(4);
@@ -1245,10 +1247,26 @@ void Writer::createMiscChunks() {
12451247
});
12461248
}
12471249

1248-
if (config->cetCompat) {
1249-
debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,
1250-
make<ExtendedDllCharacteristicsChunk>(
1251-
IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT));
1250+
uint16_t ex_characteristics_flags = 0;
1251+
if (config->cetCompat)
1252+
ex_characteristics_flags |= IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT;
1253+
if (config->cetCompatStrict)
1254+
ex_characteristics_flags |=
1255+
IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE;
1256+
if (config->cetCompatIpValidationRelaxed)
1257+
ex_characteristics_flags |=
1258+
IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE;
1259+
if (config->cetCompatDynamicApisInProcOnly)
1260+
ex_characteristics_flags |=
1261+
IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY;
1262+
if (config->hotpatchCompat)
1263+
ex_characteristics_flags |=
1264+
IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE;
1265+
1266+
if (ex_characteristics_flags) {
1267+
debugRecords.emplace_back(
1268+
COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,
1269+
make<ExtendedDllCharacteristicsChunk>(ex_characteristics_flags));
12521270
}
12531271

12541272
// Align and add each chunk referenced by the debug data directory.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// ---- /cetcompat (image is CET compatible)
2+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
3+
RUN: lld-link /out:%t.exe /entry:main /cetcompat %t.obj
4+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETCOMPAT %s
5+
6+
CHECKCETCOMPAT: DebugEntry {
7+
CHECKCETCOMPAT: Characteristics: 0x0
8+
CHECKCETCOMPAT: Type: ExtendedDLLCharacteristics (0x14)
9+
CHECKCETCOMPAT: ExtendedCharacteristics [ (0x1)
10+
CHECKCETCOMPAT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1)
11+
CHECKCETCOMPAT: ]
12+
CHECKCETCOMPAT: RawData (
13+
CHECKCETCOMPAT: 0000: 01000000 |....|
14+
CHECKCETCOMPAT: )
15+
CHECKCETCOMPAT: }
16+
17+
// ---- /cetcompat:no (image is not CET compatible)
18+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
19+
RUN: lld-link /out:%t.exe /entry:main /cetcompat:no %t.obj
20+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETCOMPAT %s
21+
22+
CHECKNOCETCOMPAT-NOT: Type: ExtendedDLLCharacteristics (0x14)
23+
CHECKNOCETCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1)
24+
25+
// ---- /cetcompatstrict (CET in strict mode)
26+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
27+
RUN: lld-link /out:%t.exe /entry:main /cetcompatstrict %t.obj
28+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETCOMPATSTRICT %s
29+
30+
CHECKCETCOMPATSTRICT: DebugEntry {
31+
CHECKCETCOMPATSTRICT: Characteristics: 0x0
32+
CHECKCETCOMPATSTRICT: Type: ExtendedDLLCharacteristics (0x14)
33+
CHECKCETCOMPATSTRICT: ExtendedCharacteristics [ (0x2)
34+
CHECKCETCOMPATSTRICT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE (0x2)
35+
CHECKCETCOMPATSTRICT: ]
36+
CHECKCETCOMPATSTRICT: RawData (
37+
CHECKCETCOMPATSTRICT: 0000: 02000000 |....|
38+
CHECKCETCOMPATSTRICT: )
39+
CHECKCETCOMPATSTRICT: }
40+
41+
// ---- /cetcompatstrict:no (image is not CET strict mode)
42+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
43+
RUN: lld-link /out:%t.exe /entry:main /cetcompatstrict:no %t.obj
44+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETSTRICT %s
45+
46+
CHECKNOCETSTRICT-NOT: Type: ExtendedDLLCharacteristics (0x14)
47+
CHECKNOCETSTRICT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE (0x2)
48+
49+
// ---- /cetdynamicapisinproc
50+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
51+
RUN: lld-link /out:%t.exe /entry:main /cetdynamicapisinproc %t.obj
52+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETDYNAPI %s
53+
54+
CHECKCETDYNAPI: DebugEntry {
55+
CHECKCETDYNAPI: Characteristics: 0x0
56+
CHECKCETDYNAPI: Type: ExtendedDLLCharacteristics (0x14)
57+
CHECKCETDYNAPI: ExtendedCharacteristics [ (0x8)
58+
CHECKCETDYNAPI: IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY (0x8)
59+
CHECKCETDYNAPI: ]
60+
CHECKCETDYNAPI: RawData (
61+
CHECKCETDYNAPI: 0000: 08000000 |....|
62+
CHECKCETDYNAPI: )
63+
CHECKCETDYNAPI: }
64+
65+
// ---- /cetdynamicapisinproc:no (image is not CET dynamic apis allowed in proc)
66+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
67+
RUN: lld-link /out:%t.exe /entry:main /cetdynamicapisinproc:no %t.obj
68+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETDYNAPI %s
69+
70+
CHECKNOCETDYNAPI-NOT: Type: ExtendedDLLCharacteristics (0x14)
71+
CHECKNOCETDYNAPI-NOT: Type: IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY (0x8)
72+
73+
// ---- /cetipvalidationrelaxed (image is not CET in context ip validation relaxed mode)
74+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
75+
RUN: lld-link /out:%t.exe /entry:main /cetipvalidationrelaxed %t.obj
76+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETIPRELAXED %s
77+
78+
CHECKCETIPRELAXED: DebugEntry {
79+
CHECKCETIPRELAXED: Characteristics: 0x0
80+
CHECKCETIPRELAXED: Type: ExtendedDLLCharacteristics (0x14)
81+
CHECKCETIPRELAXED: ExtendedCharacteristics [ (0x4)
82+
CHECKCETIPRELAXED: IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE (0x4)
83+
CHECKCETIPRELAXED: ]
84+
CHECKCETIPRELAXED: RawData (
85+
CHECKCETIPRELAXED: 0000: 04000000 |....|
86+
CHECKCETIPRELAXED: )
87+
CHECKCETIPRELAXED: }
88+
89+
// ---- /cetipvalidationrelaxed:no (image is not CET in IP validation relaxed mode)
90+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
91+
RUN: lld-link /out:%t.exe /entry:main /cetipvalidationrelaxed:no %t.obj
92+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETIPRELAXED %s
93+
94+
CHECKNOCETIPRELAXED-NOT: Type: ExtendedDLLCharacteristics (0x14)
95+
CHECKNOCETIPRELAXED-NOT: Type: IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE (0x4)
96+
97+
// ---- /hotpatchcompatible requires /functionpadmin:6
98+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
99+
RUN: lld-link /out:%t.exe /entry:main /hotpatchcompatible /functionpadmin:6 %t.obj
100+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKHOTPATCHABLE %s
101+
102+
CHECKHOTPATCHABLE: DebugEntry {
103+
CHECKHOTPATCHABLE: Characteristics: 0x0
104+
CHECKHOTPATCHABLE: Type: ExtendedDLLCharacteristics (0x14)
105+
CHECKHOTPATCHABLE: ExtendedCharacteristics [ (0x80)
106+
CHECKHOTPATCHABLE: IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE (0x80)
107+
CHECKHOTPATCHABLE: ]
108+
CHECKHOTPATCHABLE: RawData (
109+
CHECKHOTPATCHABLE: 0000: 80000000 |....|
110+
CHECKHOTPATCHABLE: )
111+
CHECKHOTPATCHABLE: }
112+
113+
// ---- /hotpatchcompatible:no (image is not hotpatch compatible)
114+
RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
115+
RUN: lld-link /out:%t.exe /entry:main /hotpatchcompatible:no %t.obj
116+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOHOTPATCHABLE %s
117+
118+
CHECKNOHOTPATCHABLE-NOT: Type: ExtendedDLLCharacteristics (0x14)
119+
CHECKNOHOTPATCHABLE-NOT: Type: IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE (0x80)
120+
121+
// ---- /hotpatchcompatible more than 6 bytes is accepted
122+
RUN: lld-link /out:%t.exe /entry:main /hotpatchcompatible /functionpadmin:10 %t.obj
123+
RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKHOTPATCHABLE2 %s
124+
125+
CHECKHOTPATCHABLE2: DebugEntry {
126+
CHECKHOTPATCHABLE2: Characteristics: 0x0
127+
CHECKHOTPATCHABLE2: Type: ExtendedDLLCharacteristics (0x14)
128+
CHECKHOTPATCHABLE2: ExtendedCharacteristics [ (0x80)
129+
CHECKHOTPATCHABLE2: IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE (0x80)
130+
CHECKHOTPATCHABLE2: ]
131+
CHECKHOTPATCHABLE2: RawData (
132+
CHECKHOTPATCHABLE2: 0000: 80000000 |....|
133+
CHECKHOTPATCHABLE2: )
134+
CHECKHOTPATCHABLE2: }
135+
136+
// ---- /hotpatchcompatible requires at least 6 bytes function padding
137+
RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /hotpatchcompatible /functionpadmin:5 %t.obj 2>&1 | FileCheck -check-prefix=CHECKHOTPATCHNOT %s
138+
CHECKHOTPATCHNOT: lld-link: error: /hotpatchcompatible: requires at least 6 bytes of /functionpadmin
139+
140+
// ---- /hotpatchcompatible requires at least 6 bytes function padding -- without /functionpadmin should raise an error
141+
RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /hotpatchcompatible %t.obj 2>&1 | FileCheck -check-prefix=CHECKHOTPATCHNOT2 %s
142+
CHECKHOTPATCHNOT2: lld-link: error: /hotpatchcompatible: requires at least 6 bytes of /functionpadmin

lld/test/COFF/options.test

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,6 @@ 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-
6353
# RUN: lld-link /out:%t.exe /entry:main /swaprun:CD %t.obj
6454
# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=SWAPCD %s
6555
# RUN: lld-link /out:%t.exe /entry:main /swaprun:cd,net %t.obj

0 commit comments

Comments
 (0)