diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index 1c338cc63fa87..bee6dc3ec3cae 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -287,6 +287,7 @@ struct Configuration { uint32_t timestamp = 0; uint32_t functionPadMin = 0; uint32_t timeTraceGranularity = 0; + uint16_t dependentLoadFlags = 0; bool dynamicBase = true; bool allowBind = true; bool cetCompat = false; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index b2d0edd8cb260..bbbaba194e83e 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -2179,6 +2179,11 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg); + // Handle /dependentloadflag + for (auto *arg : + args.filtered(OPT_dependentloadflag, OPT_dependentloadflag_opt)) + parseDependentLoadFlags(arg); + if (tar) { llvm::TimeTraceScope timeScope("Reproducer: response file"); tar->append("response.txt", diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 549407539cdde..fa54de05befb5 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -233,6 +233,9 @@ class LinkerDriver { // Parses a string in the form of "[:]" void parseFunctionPadMin(llvm::opt::Arg *a); + // Parses a string in the form of "[:]" + void parseDependentLoadFlags(llvm::opt::Arg *a); + // Parses a string in the form of "EMBED[,=]|NO". void parseManifest(StringRef arg); diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 583f6af070b62..ab10e2d1ae742 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -265,6 +265,19 @@ void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) { } } +// Parses /dependentloadflag option argument. +void LinkerDriver::parseDependentLoadFlags(llvm::opt::Arg *a) { + StringRef arg = a->getNumValues() ? a->getValue() : ""; + if (!arg.empty()) { + if (arg.getAsInteger(0, ctx.config.dependentLoadFlags)) + error("/dependentloadflag: invalid argument: " + arg); + return; + } + // MSVC linker reports error "no argument specified", although MSDN describes + // argument as optional. + error("/dependentloadflag: no argument specified"); +} + // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to // Config. diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index 977657a433dc5..abee660272689 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -56,6 +56,9 @@ def filealign : P<"filealign", "Section alignment in the output file">; def functionpadmin : F<"functionpadmin">; def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">; +def dependentloadflag : F<"dependentloadflag">; +def dependentloadflag_opt : P<"dependentloadflag", + "Sets the default load flags used to resolve the statically linked imports of a module">; def guard : P<"guard", "Control flow guard">; def heap : P<"heap", "Size of the heap">; def ignore : P<"ignore", "Specify warning codes to ignore">; diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 960328d686852..218a4f336fedc 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -264,7 +264,8 @@ class Writer { uint32_t getSizeOfInitializedData(); - void checkLoadConfig(); + void prepareLoadConfig(); + template void prepareLoadConfig(T *loadConfig); template void checkLoadConfigGuardData(const T *loadConfig); std::unique_ptr &buffer; @@ -696,7 +697,7 @@ void Writer::run() { writeHeader(); } writeSections(); - checkLoadConfig(); + prepareLoadConfig(); sortExceptionTable(); // Fix up the alignment in the TLS Directory's characteristic field, @@ -2256,7 +2257,7 @@ void Writer::fixTlsAlignment() { } } -void Writer::checkLoadConfig() { +void Writer::prepareLoadConfig() { Symbol *sym = ctx.symtab.findUnderscore("_load_config_used"); auto *b = cast_if_present(sym); if (!b) { @@ -2280,11 +2281,16 @@ void Writer::checkLoadConfig() { Twine(expectedAlign) + " bytes)"); if (ctx.config.is64()) - checkLoadConfigGuardData( - reinterpret_cast(symBuf)); + prepareLoadConfig(reinterpret_cast(symBuf)); else - checkLoadConfigGuardData( - reinterpret_cast(symBuf)); + prepareLoadConfig(reinterpret_cast(symBuf)); +} + +template void Writer::prepareLoadConfig(T *loadConfig) { + if (ctx.config.dependentLoadFlags) + loadConfig->DependentLoadFlags = ctx.config.dependentLoadFlags; + + checkLoadConfigGuardData(loadConfig); } template diff --git a/lld/test/COFF/dependentflags.test b/lld/test/COFF/dependentflags.test new file mode 100644 index 0000000000000..ef4a5cae53b28 --- /dev/null +++ b/lld/test/COFF/dependentflags.test @@ -0,0 +1,29 @@ +// ---- precomp-a.obj - x86_64, hotpatch +RUN: llvm-mc -triple x86_64-windows-msvc -filetype=obj %S/Inputs/loadconfig-cfg-x64.s -o %t.ldcfg.obj + +RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force +RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix BASE + +RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0x800 +RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-800 + +// ---- Many arguments +RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0x400 /dependentloadflag:0x800 +RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-800 + +RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0x800 /dependentloadflag:0x400 +RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-400 + +RUN: not lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag 2>&1 | FileCheck %s --check-prefix FAIL-NOARG +RUN: not lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:zz 2>&1 | FileCheck %s --check-prefix FAIL +RUN: not lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0xf0000 2>&1 | FileCheck %s --check-prefix FAIL-RANGE + + +BASE: DependentLoadFlags: 0x0 +FLAGS-800: DependentLoadFlags: 0x800 +FLAGS-400: DependentLoadFlags: 0x400 + +FAIL: lld-link: error: /dependentloadflag: invalid argument: zz +FAIL-RANGE: lld-link: error: /dependentloadflag: invalid argument: 0xf0000 +FAIL-NOARG: lld-link: error: /dependentloadflag: no argument specified + diff --git a/lld/test/COFF/deploadflag-cfg-x64.s b/lld/test/COFF/deploadflag-cfg-x64.s new file mode 100644 index 0000000000000..4d4712c3c3d78 --- /dev/null +++ b/lld/test/COFF/deploadflag-cfg-x64.s @@ -0,0 +1,31 @@ +# RUN: llvm-mc -triple x86_64-windows-msvc -filetype=obj %s -o %t.ldcfg.obj + +# RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force +# RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-400 + +# RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0x800 +# RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-800 + +# MSVC linker does not rewrite non-zero value of dependentloadflag in _load_config_used with zero +# RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0x0 +# RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-400 + +# RUN: lld-link %S/Inputs/precomp-a.obj %t.ldcfg.obj /out:%t.exe /nodefaultlib /force /dependentloadflag:0x800 /dependentloadflag:0x0 +# RUN: llvm-readobj --coff-load-config %t.exe | FileCheck %s --check-prefix FLAGS-400 + +# FLAGS-800: DependentLoadFlags: 0x800 +# FLAGS-400: DependentLoadFlags: 0x400 + + .section .rdata,"dr" +.globl _load_config_used +_load_config_used: + .long 256 + .fill 74, 1, 0 + .byte 0x00 + .byte 0x40 + .fill 48, 1, 0 + .quad __guard_fids_table + .quad __guard_fids_count + .long __guard_flags + .fill 128, 1, 0 +