aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2026-03-19 00:28:04 +0100
committerJason Donenfeld <Jason@zx2c4.com>2026-03-24 09:00:02 -0700
commit341b5e2c0261cc059b157f1c7a2a2c4d1f417f0d (patch)
treef10166b18211864ae4f94cd17b72daeb77c91639 /src/runtime
parentabd44cbe615ecea5e4bd8e6d1bb7be63d1f4b2d6 (diff)
downloadgo-341b5e2c0261cc059b157f1c7a2a2c4d1f417f0d.tar.xz
cmd/link: raise minimum windows version to 10
The minimum Windows version has been 10 for a few releases, but the PE headers weren't updated. Windows sometimes can use these in determining what kind of subsystem compatibility hacks to apply, which of course we don't want now, since Go targets Windows 10. This also causes older OSes to refuse to run the executables, rather than having them crash in some undefined way. This isn't trivial to do, because subsystem ≥ 10.0 means that the Windows loader expects to see either _load_config_used.SecurityCookie set to the initial magic value, or for IMAGE_GUARD_SECURITY_COOKIE_UNUSED to be set. Go obviously isn't making use of these features, and neither does clang/gcc for that matter; libssp doesn't even use SecurityCookie. Rather, it's exclusively for MSVC's /GS protection. So it seems like the proper thing to do is signal to the OS that it doesn't need to initialize SecurityCookie. This check lives in ntdll!LdrInitSecurityCookie. So, add the _load_config_used structure to the right PE section and give it the right flag. This lets the Windows 10-marked binaries actually run. Change-Id: I91887073c7ad01aeb0237906aafa4ea5574ac8fa Reviewed-on: https://go-review.googlesource.com/c/go/+/756680 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Jason Donenfeld <Jason@zx2c4.com> Reviewed-by: Jason Donenfeld <Jason@zx2c4.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Quim Muntal <quimmuntal@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/cgo/gcc_libinit_windows.c23
-rw-r--r--src/runtime/syscall_windows_test.go15
2 files changed, 38 insertions, 0 deletions
diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c
index 78b32254cf..ed30878c3c 100644
--- a/src/runtime/cgo/gcc_libinit_windows.c
+++ b/src/runtime/cgo/gcc_libinit_windows.c
@@ -14,6 +14,29 @@
#include "libcgo.h"
+#define IMAGE_GUARD_SECURITY_COOKIE_UNUSED 0x00000800
+// With modern mingw, we can use the normal struct:
+//
+// const IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {
+// .Size = sizeof(_load_config_used),
+// .GuardFlags = IMAGE_GUARD_SECURITY_COOKIE_UNUSED
+// };
+//
+// But we support older toolchains, so instead, fix the offsets:
+#ifdef _WIN64
+const ULONGLONG _load_config_used[40] = {
+ sizeof(_load_config_used),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ IMAGE_GUARD_SECURITY_COOKIE_UNUSED
+};
+#else
+const DWORD _load_config_used[48] = {
+ sizeof(_load_config_used),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ IMAGE_GUARD_SECURITY_COOKIE_UNUSED
+};
+#endif
+
static volatile LONG runtime_init_once_gate = 0;
static volatile LONG runtime_init_once_done = 0;
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index 77092d8fbf..437c723d51 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -8,6 +8,7 @@ import (
"fmt"
"internal/abi"
"internal/race"
+ "internal/syscall/windows"
"internal/syscall/windows/sysdll"
"internal/testenv"
"io"
@@ -1226,6 +1227,20 @@ var (
procSetEvent = modkernel32.NewProc("SetEvent")
)
+func TestTrueVersion(t *testing.T) {
+ ver, err := syscall.GetVersion()
+ if err != nil {
+ t.Fatalf("GetVersion failed: %v", err)
+ }
+ wantMajor, wantMinor, wantBuild := windows.Version()
+ major := uint32(byte(ver))
+ minor := uint32(uint8(ver >> 8))
+ build := uint32(uint16(ver >> 16))
+ if major != wantMajor || minor != wantMinor || build != wantBuild {
+ t.Errorf("GetVersion = %d.%d (Build %d), want %d.%d (Build %d)", major, minor, build, wantMajor, wantMinor, wantBuild)
+ }
+}
+
func createEvent() (syscall.Handle, error) {
r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
if r0 == 0 {