From 79bbd4dcd021fcf0393932bb908b372aafd92430 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 27 Aug 2019 08:11:01 -0600 Subject: build: backport resume monitoring for timers Signed-off-by: Jason A. Donenfeld --- Makefile | 23 +++-- build.bat | 4 + golang-runtime-resume-timers.patch | 187 +++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 golang-runtime-resume-timers.patch diff --git a/Makefile b/Makefile index c622fce8..67d85c6c 100644 --- a/Makefile +++ b/Makefile @@ -3,16 +3,31 @@ export CGO_ENABLED := 1 export CGO_CFLAGS := -O3 -Wall -Wno-unused-function -Wno-switch -std=gnu11 -DWINVER=0x0601 export CGO_LDFLAGS := -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware export GOOS := windows +OLD_GOROOT := $(GOROOT) +export GOROOT := $(CURDIR)/.deps/goroot rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) -SOURCE_FILES := $(call rwildcard,,*.go *.c *.h) +SOURCE_FILES := $(call rwildcard,,*.go *.c *.h) .deps/prepared RESOURCE_FILES := resources.rc version.h manifest.xml $(patsubst %.svg,%.ico,$(wildcard ui/icon/*.svg)) +REQUIRED_GO_VERSION := go1.13beta1 +ifneq ($(shell go version 2>/dev/null | cut -d ' ' -f 3),$(REQUIRED_GO_VERSION)) +$(error $(REQUIRED_GO_VERSION) is required) +endif + DEPLOYMENT_HOST ?= winvm DEPLOYMENT_PATH ?= Desktop all: amd64/wireguard.exe x86/wireguard.exe +.deps/prepared: export GOROOT := $(OLD_GOROOT) +.deps/prepared: $(wildcard golang-*.patch) + rm -rf .deps && mkdir -p .deps + if ! rsync --exclude=pkg/obj/go-build/trim.txt -aq $$(go env GOROOT)/ .deps/goroot; then chmod -R +w .deps/goroot; exit 1; fi + chmod -R +w .deps/goroot + cat $^ | patch -f -N -r- -p1 -d .deps/goroot + touch $@ + %.ico: %.svg convert -background none $< -define icon:auto-resize="256,128,96,64,48,32,16" $@ @@ -22,18 +37,14 @@ resources_amd64.syso: $(RESOURCE_FILES) resources_386.syso: $(RESOURCE_FILES) i686-w64-mingw32-windres -i $< -o $@ -O coff -VERSIONCHECK := @[ "$$(go version | cut -d ' ' -f 3)" == go1.13beta1 ] - amd64/wireguard.exe: export CC := x86_64-w64-mingw32-gcc amd64/wireguard.exe: export GOARCH := amd64 amd64/wireguard.exe: resources_amd64.syso $(SOURCE_FILES) - $(VERSIONCHECK) go build $(GOFLAGS) -o $@ x86/wireguard.exe: export CC := i686-w64-mingw32-gcc x86/wireguard.exe: export GOARCH := 386 x86/wireguard.exe: resources_386.syso $(SOURCE_FILES) - $(VERSIONCHECK) go build $(GOFLAGS) -o $@ remaster: export CC := x86_64-w64-mingw32-gcc @@ -54,6 +65,6 @@ deploy: amd64/wireguard.exe scp $< $(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH) clean: - rm -rf *.syso ui/icon/*.ico x86/ amd64/ .deps + rm -rf *.syso ui/icon/*.ico x86/ amd64/ .deps/ .PHONY: deploy clean fmt remaster all diff --git a/build.bat b/build.bat index 3361b28b..3c1cba66 100644 --- a/build.bat +++ b/build.bat @@ -23,6 +23,10 @@ if exist .deps\prepared goto :render rem Mirror of https://sourceforge.net/projects/ezwinports/files/make-4.2.1-without-guile-w32-bin.zip call :download make.zip https://download.wireguard.com/windows-toolchain/distfiles/make-4.2.1-without-guile-w32-bin.zip 30641be9602712be76212b99df7209f4f8f518ba764cf564262bc9d6e4047cc7 "--strip-components 1 bin" || goto :error call :download wireguard-tools.zip https://git.zx2c4.com/WireGuard/snapshot/WireGuard-0.0.20190702.zip 2fb45e145f36e4e965f8acb48061603a2192c6729266cbcd9f9669024e433588 "--exclude wg-quick --strip-components 1" || goto :error + rem Mirror of https://sourceforge.net/projects/gnuwin32/files/patch/2.5.9-7/patch-2.5.9-7-bin.zip with fixed manifest + call :download patch.zip https://download.wireguard.com/windows-toolchain/distfiles/patch-2.5.9-7-bin-fixed-manifest.zip 25977006ca9713f2662a5d0a2ed3a5a138225b8be3757035bd7da9dcf985d0a1 "--strip-components 1 bin" || goto :error + echo [+] Patching go + for %%a in ("..\golang-*.patch") do .\patch.exe -f -N -r- -d go -p1 --binary < "%%a" || goto :error copy /y NUL prepared > NUL || goto :error cd .. || goto :error diff --git a/golang-runtime-resume-timers.patch b/golang-runtime-resume-timers.patch new file mode 100644 index 00000000..17e09bb3 --- /dev/null +++ b/golang-runtime-resume-timers.patch @@ -0,0 +1,187 @@ +From e7384a38af94ff5e7e748f1424d766e1d3316f8d Mon Sep 17 00:00:00 2001 +From: Jason A. Donenfeld +Date: Tue, 27 Aug 2019 06:46:16 -0600 +Subject: [PATCH] runtime: monitor for suspend/resume to kick timeouts + +Starting in Windows 8, the wait functions don't take into account +suspend time, even though the monotonic counters do. This results in +timer buckets stalling on resume. Therefore, this commit makes it so +that on resume, we return from the wait functions and recalculate the +amount of time left to wait. + +Fixes: #31528 + +Change-Id: I0db02cc72188cb620954e87a0180e0a3c83f4a56 +--- + +diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go +index 074ae0f..6cc9670 100644 +--- a/src/runtime/os_windows.go ++++ b/src/runtime/os_windows.go +@@ -49,6 +49,7 @@ + //go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll" + //go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll" + //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" ++//go:cgo_import_dynamic runtime._WaitForMultipleObjects WaitForMultipleObjects%4 "kernel32.dll" + //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" + //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" + +@@ -96,6 +97,7 @@ + _VirtualFree, + _VirtualQuery, + _WaitForSingleObject, ++ _WaitForMultipleObjects, + _WriteConsoleW, + _WriteFile, + _ stdFunction +@@ -139,7 +141,8 @@ + func ctrlhandler() + + type mOS struct { +- waitsema uintptr // semaphore for parking on locks ++ waitsema uintptr // semaphore for parking on locks ++ resumesema uintptr // semaphore to indicate suspend/resume + } + + //go:linkname os_sigpipe os.sigpipe +@@ -258,6 +261,34 @@ + } + } + ++func monitorSuspendResume() { ++ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) ++ if powrprof == 0 { ++ return // Running on Windows 7, where we don't need it anyway. ++ } ++ powerRegisterSuspendResumeNotification := windowsFindfunc(powrprof, []byte("PowerRegisterSuspendResumeNotification\000")) ++ if powerRegisterSuspendResumeNotification == nil { ++ return // Running on Windows 7, where we don't need it anyway. ++ } ++ var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr { ++ lock(&allglock) ++ for _, gp := range allgs { ++ if gp.m != nil && gp.m.resumesema != 0 { ++ stdcall1(_SetEvent, gp.m.resumesema) ++ } ++ } ++ unlock(&allglock) ++ return 0 ++ } ++ var handle uintptr ++ callback := compileCallback(*efaceOf(&fn), true) ++ if stdcall3(powerRegisterSuspendResumeNotification, 2, ++ uintptr(unsafe.Pointer(&[2]uintptr{callback, 0})), ++ uintptr(unsafe.Pointer(&handle))) != 0 { ++ throw("PowerRegisterSuspendResumeNotification failure") ++ } ++} ++ + //go:nosplit + func getLoadLibrary() uintptr { + return uintptr(unsafe.Pointer(_LoadLibraryW)) +@@ -488,6 +519,10 @@ + } + + stdcall1(_FreeEnvironmentStringsW, uintptr(strings)) ++ ++ // We call this all the way here, late in init, so that malloc works ++ // for the callback function this generates. ++ monitorSuspendResume() + } + + // exiting is set to non-zero when the process is exiting. +@@ -606,21 +641,34 @@ + _WAIT_FAILED = 0xFFFFFFFF + ) + +- // store ms in ns to save stack space ++ var result uintptr ++ var elapsed, start int64 ++retry: + if ns < 0 { +- ns = _INFINITE ++ result = stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(_INFINITE)) + } else { +- ns = int64(timediv(ns, 1000000, nil)) +- if ns == 0 { +- ns = 1 ++ if start == 0 { ++ start = nanotime() + } ++ ms := int64(timediv(ns-elapsed, 1000000, nil)) ++ if ms == 0 { ++ ms = 1 ++ } ++ result = stdcall4(_WaitForMultipleObjects, 2, ++ uintptr(unsafe.Pointer(&[2]uintptr{getg().m.waitsema, getg().m.resumesema})), ++ 0, uintptr(ms)) + } +- +- result := stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) + switch result { + case _WAIT_OBJECT_0: //signaled + return 0 + ++ case _WAIT_OBJECT_0 + 1: //system resume ++ elapsed = nanotime() - start ++ if elapsed >= ns { ++ return -1 ++ } ++ goto retry ++ + case _WAIT_TIMEOUT: + return -1 + +@@ -667,6 +715,15 @@ + throw("runtime.semacreate") + }) + } ++ mp.resumesema = stdcall4(_CreateEventA, 0, 0, 0, 0) ++ if mp.resumesema == 0 { ++ systemstack(func() { ++ print("runtime: createevent failed; errno=", getlasterror(), "\n") ++ throw("runtime.semacreate") ++ }) ++ stdcall1(_CloseHandle, mp.waitsema) ++ mp.waitsema = 0 ++ } + } + + // May run with m.p==nil, so write barriers are not allowed. This +diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go +index 722a73d..0e2fcfb 100644 +--- a/src/runtime/syscall_windows.go ++++ b/src/runtime/syscall_windows.go +@@ -74,16 +74,18 @@ + argsize += uintptrSize + } + +- lock(&cbs.lock) +- defer unlock(&cbs.lock) ++ lock(&cbs.lock) // We don't unlock this in a defer because this is used from the system stack. + + n := cbs.n + for i := 0; i < n; i++ { + if cbs.ctxt[i].gobody == fn.data && cbs.ctxt[i].isCleanstack() == cleanstack { +- return callbackasmAddr(i) ++ r := callbackasmAddr(i) ++ unlock(&cbs.lock) ++ return r + } + } + if n >= cb_max { ++ unlock(&cbs.lock) + throw("too many callback functions") + } + +@@ -99,7 +101,9 @@ + cbs.ctxt[n] = c + cbs.n++ + +- return callbackasmAddr(n) ++ r := callbackasmAddr(n) ++ unlock(&cbs.lock) ++ return r + } + + const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -- cgit v1.2.3-59-g8ed1b