diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index 91b6e632fa7ed..95491c51bb7cd 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -307,6 +307,10 @@ struct Configuration { bool dynamicBase = true; bool allowBind = true; bool cetCompat = false; + bool cetCompatStrict = false; + bool cetCompatIpValidationRelaxed = false; + bool cetCompatDynamicApisInProcOnly = false; + bool hotpatchCompat = false; bool nxCompat = true; bool allowIsolation = true; bool terminalServerAware = true; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 570b8f9d05906..350430067f5c0 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -2145,6 +2145,14 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { config->integrityCheck = args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false); + config->cetCompatStrict = + args.hasFlag(OPT_cetcompatstrict, OPT_cetcompatstrict_no, false); + config->cetCompatIpValidationRelaxed = args.hasFlag( + OPT_cetipvalidationrelaxed, OPT_cetipvalidationrelaxed_no, false); + config->cetCompatDynamicApisInProcOnly = args.hasFlag( + OPT_cetdynamicapisinproc, OPT_cetdynamicapisinproc_no, false); + config->hotpatchCompat = + args.hasFlag(OPT_hotpatchcompatible, OPT_hotpatchcompatible_no, false); config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); for (auto *arg : args.filtered(OPT_swaprun)) parseSwaprun(arg->getValue()); @@ -2298,6 +2306,12 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg); + // MS link.exe compatibility, at least 6 bytes of function padding is + // required if hotpatchable + if (config->hotpatchCompat && config->functionPadMin < 6) + Err(ctx) + << "/hotpatchcompatible: requires at least 6 bytes of /functionpadmin"; + // Handle /dependentloadflag for (auto *arg : args.filtered(OPT_dependentloadflag, OPT_dependentloadflag_opt)) diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index 0d66b49a4fdb8..e1b2b67323160 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -196,6 +196,12 @@ defm appcontainer : B<"appcontainer", "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 cetcompatstrict : B<"cetcompatstrict", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack in strict mode", + "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack in strict mode (default)">; +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", + "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack with dynamic APIs allowed in process (default)">; +defm cetipvalidationrelaxed : B<"cetipvalidationrelaxed", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack with relaxed context IP validation", + "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack with relaxed context IP validation (default)">; defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)", "Disable ASLR (default when /fixed)">; defm fixed : B<"fixed", "Disable base relocations", @@ -203,6 +209,8 @@ defm fixed : B<"fixed", "Disable base relocations", defm highentropyva : B<"highentropyva", "Enable 64-bit ASLR (default on 64-bit)", "Disable 64-bit ASLR">; +defm hotpatchcompatible : B<"hotpatchcompatible", "Mark executable image as compatible with hotpatch", + "Don't mark executable image as compatible with hotpatch (default)">; defm incremental : B<"incremental", "Keep original import library if contents are unchanged", "Overwrite import library even if contents are unchanged">; diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 076561807af47..0ab1b1bd35fa0 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1216,7 +1216,9 @@ void Writer::createMiscChunks() { // Create Debug Information Chunks debugInfoSec = config->mingw ? buildidSec : rdataSec; if (config->buildIDHash != BuildIDHash::None || config->debug || - config->repro || config->cetCompat) { + config->repro || config->cetCompat || config->cetCompatStrict || + config->cetCompatIpValidationRelaxed || + config->cetCompatDynamicApisInProcOnly || config->hotpatchCompat) { debugDirectory = make(ctx, debugRecords, config->repro); debugDirectory->setAlignment(4); @@ -1237,10 +1239,26 @@ void Writer::createMiscChunks() { }); } - if (config->cetCompat) { - debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, - make( - IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT)); + uint16_t ex_characteristics_flags = 0; + if (config->cetCompat) + ex_characteristics_flags |= IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT; + if (config->cetCompatStrict) + ex_characteristics_flags |= + IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE; + if (config->cetCompatIpValidationRelaxed) + ex_characteristics_flags |= + IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE; + if (config->cetCompatDynamicApisInProcOnly) + ex_characteristics_flags |= + IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY; + if (config->hotpatchCompat) + ex_characteristics_flags |= + IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE; + + if (ex_characteristics_flags) { + debugRecords.emplace_back( + COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, + make(ex_characteristics_flags)); } // Align and add each chunk referenced by the debug data directory. diff --git a/lld/test/COFF/exdllcharacteristics.test b/lld/test/COFF/exdllcharacteristics.test new file mode 100644 index 0000000000000..ef5a90ca9d519 --- /dev/null +++ b/lld/test/COFF/exdllcharacteristics.test @@ -0,0 +1,142 @@ +// ---- /cetcompat (image is CET compatible) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetcompat %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETCOMPAT %s + +CHECKCETCOMPAT: DebugEntry { +CHECKCETCOMPAT: Characteristics: 0x0 +CHECKCETCOMPAT: Type: ExtendedDLLCharacteristics (0x14) +CHECKCETCOMPAT: ExtendedCharacteristics [ (0x1) +CHECKCETCOMPAT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1) +CHECKCETCOMPAT: ] +CHECKCETCOMPAT: RawData ( +CHECKCETCOMPAT: 0000: 01000000 |....| +CHECKCETCOMPAT: ) +CHECKCETCOMPAT: } + +// ---- /cetcompat:no (image is not CET compatible) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetcompat:no %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETCOMPAT %s + +CHECKNOCETCOMPAT-NOT: Type: ExtendedDLLCharacteristics (0x14) +CHECKNOCETCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1) + +// ---- /cetcompatstrict (CET in strict mode) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetcompatstrict %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETCOMPATSTRICT %s + +CHECKCETCOMPATSTRICT: DebugEntry { +CHECKCETCOMPATSTRICT: Characteristics: 0x0 +CHECKCETCOMPATSTRICT: Type: ExtendedDLLCharacteristics (0x14) +CHECKCETCOMPATSTRICT: ExtendedCharacteristics [ (0x2) +CHECKCETCOMPATSTRICT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE (0x2) +CHECKCETCOMPATSTRICT: ] +CHECKCETCOMPATSTRICT: RawData ( +CHECKCETCOMPATSTRICT: 0000: 02000000 |....| +CHECKCETCOMPATSTRICT: ) +CHECKCETCOMPATSTRICT: } + +// ---- /cetcompatstrict:no (image is not CET strict mode) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetcompatstrict:no %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETSTRICT %s + +CHECKNOCETSTRICT-NOT: Type: ExtendedDLLCharacteristics (0x14) +CHECKNOCETSTRICT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE (0x2) + +// ---- /cetdynamicapisinproc +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetdynamicapisinproc %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETDYNAPI %s + +CHECKCETDYNAPI: DebugEntry { +CHECKCETDYNAPI: Characteristics: 0x0 +CHECKCETDYNAPI: Type: ExtendedDLLCharacteristics (0x14) +CHECKCETDYNAPI: ExtendedCharacteristics [ (0x8) +CHECKCETDYNAPI: IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY (0x8) +CHECKCETDYNAPI: ] +CHECKCETDYNAPI: RawData ( +CHECKCETDYNAPI: 0000: 08000000 |....| +CHECKCETDYNAPI: ) +CHECKCETDYNAPI: } + +// ---- /cetdynamicapisinproc:no (image is not CET dynamic apis allowed in proc) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetdynamicapisinproc:no %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETDYNAPI %s + +CHECKNOCETDYNAPI-NOT: Type: ExtendedDLLCharacteristics (0x14) +CHECKNOCETDYNAPI-NOT: Type: IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY (0x8) + +// ---- /cetipvalidationrelaxed (image is not CET in context ip validation relaxed mode) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetipvalidationrelaxed %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKCETIPRELAXED %s + +CHECKCETIPRELAXED: DebugEntry { +CHECKCETIPRELAXED: Characteristics: 0x0 +CHECKCETIPRELAXED: Type: ExtendedDLLCharacteristics (0x14) +CHECKCETIPRELAXED: ExtendedCharacteristics [ (0x4) +CHECKCETIPRELAXED: IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE (0x4) +CHECKCETIPRELAXED: ] +CHECKCETIPRELAXED: RawData ( +CHECKCETIPRELAXED: 0000: 04000000 |....| +CHECKCETIPRELAXED: ) +CHECKCETIPRELAXED: } + +// ---- /cetipvalidationrelaxed:no (image is not CET in IP validation relaxed mode) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /cetipvalidationrelaxed:no %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOCETIPRELAXED %s + +CHECKNOCETIPRELAXED-NOT: Type: ExtendedDLLCharacteristics (0x14) +CHECKNOCETIPRELAXED-NOT: Type: IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE (0x4) + +// ---- /hotpatchcompatible requires /functionpadmin:6 +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /hotpatchcompatible /functionpadmin:6 %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKHOTPATCHABLE %s + +CHECKHOTPATCHABLE: DebugEntry { +CHECKHOTPATCHABLE: Characteristics: 0x0 +CHECKHOTPATCHABLE: Type: ExtendedDLLCharacteristics (0x14) +CHECKHOTPATCHABLE: ExtendedCharacteristics [ (0x80) +CHECKHOTPATCHABLE: IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE (0x80) +CHECKHOTPATCHABLE: ] +CHECKHOTPATCHABLE: RawData ( +CHECKHOTPATCHABLE: 0000: 80000000 |....| +CHECKHOTPATCHABLE: ) +CHECKHOTPATCHABLE: } + +// ---- /hotpatchcompatible:no (image is not hotpatch compatible) +RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +RUN: lld-link /out:%t.exe /entry:main /hotpatchcompatible:no %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKNOHOTPATCHABLE %s + +CHECKNOHOTPATCHABLE-NOT: Type: ExtendedDLLCharacteristics (0x14) +CHECKNOHOTPATCHABLE-NOT: Type: IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE (0x80) + +// ---- /hotpatchcompatible more than 6 bytes is accepted +RUN: lld-link /out:%t.exe /entry:main /hotpatchcompatible /functionpadmin:10 %t.obj +RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CHECKHOTPATCHABLE2 %s + +CHECKHOTPATCHABLE2: DebugEntry { +CHECKHOTPATCHABLE2: Characteristics: 0x0 +CHECKHOTPATCHABLE2: Type: ExtendedDLLCharacteristics (0x14) +CHECKHOTPATCHABLE2: ExtendedCharacteristics [ (0x80) +CHECKHOTPATCHABLE2: IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE (0x80) +CHECKHOTPATCHABLE2: ] +CHECKHOTPATCHABLE2: RawData ( +CHECKHOTPATCHABLE2: 0000: 80000000 |....| +CHECKHOTPATCHABLE2: ) +CHECKHOTPATCHABLE2: } + +// ---- /hotpatchcompatible requires at least 6 bytes function padding +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 +CHECKHOTPATCHNOT: lld-link: error: /hotpatchcompatible: requires at least 6 bytes of /functionpadmin + +// ---- /hotpatchcompatible requires at least 6 bytes function padding -- without /functionpadmin should raise an error +RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /hotpatchcompatible %t.obj 2>&1 | FileCheck -check-prefix=CHECKHOTPATCHNOT2 %s +CHECKHOTPATCHNOT2: lld-link: error: /hotpatchcompatible: requires at least 6 bytes of /functionpadmin diff --git a/lld/test/COFF/options.test b/lld/test/COFF/options.test index 0dd889042869a..c21ae9685a85c 100644 --- a/lld/test/COFF/options.test +++ b/lld/test/COFF/options.test @@ -50,16 +50,6 @@ 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