aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2016-04-05 16:37:29 -0400
committerAustin Clements <austin@google.com>2016-04-05 16:37:54 -0400
commit6d6e16001bb04d4bf60c6d14d1f64684a043ef6c (patch)
tree53d7dd0bbe4f919fec4a15bde9811a9608b54627 /src/runtime
parent22972d2207424edc481b7c127788f573a726dfe7 (diff)
parent5e1b7bdecf7a8b5b5d06633758bf53e475902414 (diff)
downloadgo-6d6e16001bb04d4bf60c6d14d1f64684a043ef6c.tar.xz
[dev.garbage] Merge branch 'master' into dev.garbage
Change-Id: I47ac4112befc07d3674d7a88827227199edd93b4
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/asm_386.s10
-rw-r--r--src/runtime/asm_amd64.s21
-rw-r--r--src/runtime/cgo.go2
-rw-r--r--src/runtime/cgo/callbacks_traceback.go17
-rw-r--r--src/runtime/cgo/gcc_libinit_windows.c80
-rw-r--r--src/runtime/cgo/gcc_traceback.c29
-rw-r--r--src/runtime/cgocall.go7
-rw-r--r--src/runtime/chan_test.go29
-rw-r--r--src/runtime/crash_cgo_test.go12
-rw-r--r--src/runtime/export_test.go16
-rw-r--r--src/runtime/export_windows_test.go5
-rw-r--r--src/runtime/gc_test.go17
-rw-r--r--src/runtime/iface.go67
-rw-r--r--src/runtime/mbarrier.go25
-rw-r--r--src/runtime/memmove_386.s19
-rw-r--r--src/runtime/memmove_amd64.s24
-rw-r--r--src/runtime/mgcsweep.go2
-rw-r--r--src/runtime/mheap.go2
-rw-r--r--src/runtime/os1_linux.go9
-rw-r--r--src/runtime/os1_windows.go92
-rw-r--r--src/runtime/proc.go54
-rw-r--r--src/runtime/rt0_windows_386.s34
-rw-r--r--src/runtime/rt0_windows_amd64.s31
-rw-r--r--src/runtime/runtime2.go9
-rw-r--r--src/runtime/signal_solaris.go2
-rw-r--r--src/runtime/string.go3
-rw-r--r--src/runtime/symtab.go1
-rw-r--r--src/runtime/sys_linux_386.s3
-rw-r--r--src/runtime/sys_linux_amd64.s59
-rw-r--r--src/runtime/sys_linux_arm.s4
-rw-r--r--src/runtime/sys_linux_arm64.s4
-rw-r--r--src/runtime/sys_linux_mips64x.s4
-rw-r--r--src/runtime/sys_linux_ppc64x.s15
-rw-r--r--src/runtime/sys_windows_386.s17
-rw-r--r--src/runtime/sys_windows_amd64.s20
-rw-r--r--src/runtime/syscall_windows.go35
-rw-r--r--src/runtime/syscall_windows_test.go245
-rw-r--r--src/runtime/testdata/testprogcgo/threadpanic_windows.c3
-rw-r--r--src/runtime/testdata/testprogcgo/traceback.go80
-rw-r--r--src/runtime/traceback.go247
-rw-r--r--src/runtime/wbfat.go190
-rw-r--r--src/runtime/wbfat_gen.go41
42 files changed, 1231 insertions, 355 deletions
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s
index 2d16f4940a..dec79189bc 100644
--- a/src/runtime/asm_386.s
+++ b/src/runtime/asm_386.s
@@ -54,6 +54,7 @@ bad_proc: // show that the program requires MMX.
has_cpuid:
MOVL $0, AX
CPUID
+ MOVL AX, SI
CMPL AX, $0
JE nocpuinfo
@@ -69,6 +70,7 @@ has_cpuid:
MOVB $1, runtime·lfenceBeforeRdtsc(SB)
notintel:
+ // Load EAX=1 cpuid flags
MOVL $1, AX
CPUID
MOVL CX, AX // Move to global variable clobbers CX when generating PIC
@@ -79,6 +81,14 @@ notintel:
TESTL $(1<<23), DX // MMX
JZ bad_proc
+ // Load EAX=7/ECX=0 cpuid flags
+ CMPL SI, $7
+ JLT nocpuinfo
+ MOVL $7, AX
+ MOVL $0, CX
+ CPUID
+ MOVL BX, runtime·cpuid_ebx7(SB)
+
nocpuinfo:
// if there is an _cgo_init, call it to let it
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index b4df1d80d7..83db4d3e81 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -28,6 +28,7 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
// find out information about the processor we're on
MOVQ $0, AX
CPUID
+ MOVQ AX, SI
CMPQ AX, $0
JE nocpuinfo
@@ -42,15 +43,25 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
JNE notintel
MOVB $1, runtime·lfenceBeforeRdtsc(SB)
notintel:
- // Do nothing.
+ // Load EAX=1 cpuid flags
MOVQ $1, AX
CPUID
MOVL CX, runtime·cpuid_ecx(SB)
MOVL DX, runtime·cpuid_edx(SB)
+
+ // Load EAX=7/ECX=0 cpuid flags
+ CMPQ SI, $7
+ JLT no7
+ MOVL $7, AX
+ MOVL $0, CX
+ CPUID
+ MOVL BX, runtime·cpuid_ebx7(SB)
+no7:
// Detect AVX and AVX2 as per 14.7.1 Detection of AVX2 chapter of [1]
// [1] 64-ia-32-architectures-software-developer-manual-325462.pdf
// http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf
+ MOVL runtime·cpuid_ecx(SB), CX
ANDL $0x18000000, CX // check for OSXSAVE and AVX bits
CMPL CX, $0x18000000
JNE noavx
@@ -61,12 +72,8 @@ notintel:
CMPL AX, $6 // Check for OS support of YMM registers
JNE noavx
MOVB $1, runtime·support_avx(SB)
- MOVL $7, AX
- MOVL $0, CX
- CPUID
- ANDL $0x20, BX // check for AVX2 bit
- CMPL BX, $0x20
- JNE noavx2
+ TESTL $(1<<5), runtime·cpuid_ebx7(SB) // check for AVX2 bit
+ JEQ noavx2
MOVB $1, runtime·support_avx2(SB)
JMP nocpuinfo
noavx:
diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go
index 8ba31cd481..35d7a07e15 100644
--- a/src/runtime/cgo.go
+++ b/src/runtime/cgo.go
@@ -16,6 +16,7 @@ import "unsafe"
//go:linkname _cgo_thread_start _cgo_thread_start
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
+//go:linkname _cgo_callers _cgo_callers
var (
_cgo_init unsafe.Pointer
@@ -24,6 +25,7 @@ var (
_cgo_thread_start unsafe.Pointer
_cgo_sys_thread_create unsafe.Pointer
_cgo_notify_runtime_init_done unsafe.Pointer
+ _cgo_callers unsafe.Pointer
)
// iscgo is set to true by the runtime/cgo package
diff --git a/src/runtime/cgo/callbacks_traceback.go b/src/runtime/cgo/callbacks_traceback.go
new file mode 100644
index 0000000000..f754846722
--- /dev/null
+++ b/src/runtime/cgo/callbacks_traceback.go
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package cgo
+
+import _ "unsafe" // for go:linkname
+
+// Calls the traceback function passed to SetCgoTraceback.
+
+//go:cgo_import_static x_cgo_callers
+//go:linkname x_cgo_callers x_cgo_callers
+//go:linkname _cgo_callers _cgo_callers
+var x_cgo_callers byte
+var _cgo_callers = &x_cgo_callers
diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c
index eb798ce5e8..50887b844d 100644
--- a/src/runtime/cgo/gcc_libinit_windows.c
+++ b/src/runtime/cgo/gcc_libinit_windows.c
@@ -2,21 +2,89 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build cgo
+#define WIN64_LEAN_AND_MEAN
+#include <windows.h>
+#include <process.h>
+
#include <stdio.h>
#include <stdlib.h>
+static volatile long runtime_init_once_gate = 0;
+static volatile long runtime_init_once_done = 0;
+
+static CRITICAL_SECTION runtime_init_cs;
+
+static HANDLE runtime_init_wait;
+static int runtime_init_done;
+
+// Pre-initialize the runtime synchronization objects
+void
+_cgo_preinit_init() {
+ runtime_init_wait = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (runtime_init_wait == NULL) {
+ fprintf(stderr, "runtime: failed to create runtime initialization wait event.\n");
+ abort();
+ }
+
+ InitializeCriticalSection(&runtime_init_cs);
+}
+
+// Make sure that the preinit sequence has run.
+void
+_cgo_maybe_run_preinit() {
+ if (!InterlockedExchangeAdd(&runtime_init_once_done, 0)) {
+ if (InterlockedIncrement(&runtime_init_once_gate) == 1) {
+ _cgo_preinit_init();
+ InterlockedIncrement(&runtime_init_once_done);
+ } else {
+ // Decrement to avoid overflow.
+ InterlockedDecrement(&runtime_init_once_gate);
+ while(!InterlockedExchangeAdd(&runtime_init_once_done, 0)) {
+ Sleep(0);
+ }
+ }
+ }
+}
+
void
-x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
- fprintf(stderr, "x_cgo_sys_thread_create not implemented");
- abort();
+x_cgo_sys_thread_create(void (*func)(void*), void* arg) {
+ uintptr_t thandle;
+
+ thandle = _beginthread(func, 0, arg);
+ if(thandle == -1) {
+ fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno);
+ abort();
+ }
+}
+
+int
+_cgo_is_runtime_initialized() {
+ EnterCriticalSection(&runtime_init_cs);
+ int status = runtime_init_done;
+ LeaveCriticalSection(&runtime_init_cs);
+ return status;
}
void
_cgo_wait_runtime_init_done() {
- // TODO(spetrovic): implement this method.
+ _cgo_maybe_run_preinit();
+ while (!_cgo_is_runtime_initialized()) {
+ WaitForSingleObject(runtime_init_wait, INFINITE);
+ }
}
void
x_cgo_notify_runtime_init_done(void* dummy) {
- // TODO(spetrovic): implement this method.
-} \ No newline at end of file
+ _cgo_maybe_run_preinit();
+
+ EnterCriticalSection(&runtime_init_cs);
+ runtime_init_done = 1;
+ LeaveCriticalSection(&runtime_init_cs);
+
+ if (!SetEvent(runtime_init_wait)) {
+ fprintf(stderr, "runtime: failed to signal runtime initialization complete.\n");
+ abort();
+ }
+}
+
diff --git a/src/runtime/cgo/gcc_traceback.c b/src/runtime/cgo/gcc_traceback.c
new file mode 100644
index 0000000000..4fdfbe4d9f
--- /dev/null
+++ b/src/runtime/cgo/gcc_traceback.c
@@ -0,0 +1,29 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+// +build linux
+
+#include <stdint.h>
+
+struct cgoTracebackArg {
+ uintptr_t Context;
+ uintptr_t* Buf;
+ uintptr_t Max;
+};
+
+// Call the user's traceback function and then call sigtramp.
+// The runtime signal handler will jump to this code.
+// We do it this way so that the user's traceback function will be called
+// by a C function with proper unwind info.
+void
+x_cgo_callers(uintptr_t sig, void *info, void *context, void (*cgoTraceback)(struct cgoTracebackArg*), uintptr_t* cgoCallers, void (*sigtramp)(uintptr_t, void*, void*)) {
+ struct cgoTracebackArg arg;
+
+ arg.Context = 0;
+ arg.Buf = cgoCallers;
+ arg.Max = 32; // must match len(runtime.cgoCallers)
+ (*cgoTraceback)(&arg);
+ sigtramp(sig, info, context);
+}
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index 7a683d7524..d5248803a4 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -84,6 +84,10 @@ import (
"unsafe"
)
+// Addresses collected in a cgo backtrace when crashing.
+// Length must match arg.Max in x_cgo_callers in runtime/cgo/gcc_traceback.c.
+type cgoCallers [32]uintptr
+
// Call from Go to C.
//go:nosplit
func cgocall(fn, arg unsafe.Pointer) int32 {
@@ -109,6 +113,9 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
mp.ncgo++
defer endcgo(mp)
+ // Reset traceback.
+ mp.cgoCallers[0] = 0
+
/*
* Announce we are entering a system call
* so that the scheduler knows to create another
diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go
index 911821bea5..219f2b449b 100644
--- a/src/runtime/chan_test.go
+++ b/src/runtime/chan_test.go
@@ -777,7 +777,7 @@ func BenchmarkChanContended(b *testing.B) {
})
}
-func BenchmarkChanSync(b *testing.B) {
+func benchmarkChanSync(b *testing.B, work int) {
const CallsPerSched = 1000
procs := 2
N := int32(b.N / CallsPerSched / procs * procs)
@@ -793,10 +793,14 @@ func BenchmarkChanSync(b *testing.B) {
for g := 0; g < CallsPerSched; g++ {
if i%2 == 0 {
<-myc
+ localWork(work)
myc <- 0
+ localWork(work)
} else {
myc <- 0
+ localWork(work)
<-myc
+ localWork(work)
}
}
}
@@ -808,6 +812,14 @@ func BenchmarkChanSync(b *testing.B) {
}
}
+func BenchmarkChanSync(b *testing.B) {
+ benchmarkChanSync(b, 0)
+}
+
+func BenchmarkChanSyncWork(b *testing.B) {
+ benchmarkChanSync(b, 1000)
+}
+
func benchmarkChanProdCons(b *testing.B, chanSize, localWork int) {
const CallsPerSched = 1000
procs := runtime.GOMAXPROCS(-1)
@@ -981,3 +993,18 @@ func BenchmarkChanPopular(b *testing.B) {
}
wg.Wait()
}
+
+var (
+ alwaysFalse = false
+ workSink = 0
+)
+
+func localWork(w int) {
+ foo := 0
+ for i := 0; i < w; i++ {
+ foo /= (foo + 1)
+ }
+ if alwaysFalse {
+ workSink += foo
+ }
+}
diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go
index 2f7591a8d3..6547996b43 100644
--- a/src/runtime/crash_cgo_test.go
+++ b/src/runtime/crash_cgo_test.go
@@ -209,3 +209,15 @@ func TestCgoCCodeSIGPROF(t *testing.T) {
t.Errorf("expected %q got %v", want, got)
}
}
+
+func TestCgoCrashTraceback(t *testing.T) {
+ if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+ t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+ got := runTestProg(t, "testprogcgo", "CrashTraceback")
+ for i := 1; i <= 3; i++ {
+ if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
+ t.Errorf("missing cgo symbolizer:%d", i)
+ }
+ }
+}
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 3994d5caf8..fd33c9c3c8 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -196,3 +196,19 @@ func SetTracebackEnv(level string) {
var ReadUnaligned32 = readUnaligned32
var ReadUnaligned64 = readUnaligned64
+
+func CountPagesInUse() (pagesInUse, counted uintptr) {
+ stopTheWorld("CountPagesInUse")
+
+ pagesInUse = uintptr(mheap_.pagesInUse)
+
+ for _, s := range h_allspans {
+ if s.state == mSpanInUse {
+ counted += s.npages
+ }
+ }
+
+ startTheWorld()
+
+ return
+}
diff --git a/src/runtime/export_windows_test.go b/src/runtime/export_windows_test.go
index 9e744fc35f..66c103709c 100644
--- a/src/runtime/export_windows_test.go
+++ b/src/runtime/export_windows_test.go
@@ -9,9 +9,14 @@ package runtime
import "unsafe"
var TestingWER = &testingWER
+var OsYield = osyield
func NumberOfProcessors() int32 {
var info systeminfo
stdcall1(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
return int32(info.dwnumberofprocessors)
}
+
+func LoadLibraryExStatus() (useEx, haveEx, haveFlags bool) {
+ return useLoadLibraryEx, _LoadLibraryExW != nil, _AddDllDirectory != nil
+}
diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go
index c8c96bb4ee..d53d3ee000 100644
--- a/src/runtime/gc_test.go
+++ b/src/runtime/gc_test.go
@@ -473,3 +473,20 @@ func testIfaceEqual(x interface{}) {
a = true
}
}
+
+func TestPageAccounting(t *testing.T) {
+ // Grow the heap in small increments. This used to drop the
+ // pages-in-use count below zero because of a rounding
+ // mismatch (golang.org/issue/15022).
+ const blockSize = 64 << 10
+ blocks := make([]*[blockSize]byte, (64<<20)/blockSize)
+ for i := range blocks {
+ blocks[i] = new([blockSize]byte)
+ }
+
+ // Check that the running page count matches reality.
+ pagesInUse, counted := runtime.CountPagesInUse()
+ if pagesInUse != counted {
+ t.Fatalf("mheap_.pagesInUse is %d, but direct count is %d", pagesInUse, counted)
+ }
+}
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index ced87ea816..3ce1e237d3 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -19,25 +19,28 @@ var (
hash [hashSize]*itab
)
+func itabhash(inter *interfacetype, typ *_type) uint32 {
+ // compiler has provided some good hash codes for us.
+ h := inter.typ.hash
+ h += 17 * typ.hash
+ // TODO(rsc): h += 23 * x.mhash ?
+ return h % hashSize
+}
+
func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
if len(inter.mhdr) == 0 {
throw("internal error - misuse of itab")
}
// easy case
- x := typ.uncommon()
- if x == nil {
+ if typ.tflag&tflagUncommon == 0 {
if canfail {
return nil
}
panic(&TypeAssertionError{"", typ._string, inter.typ._string, inter.mhdr[0].name.name()})
}
- // compiler has provided some good hash codes for us.
- h := inter.typ.hash
- h += 17 * typ.hash
- // TODO(rsc): h += 23 * x.mhash ?
- h %= hashSize
+ h := itabhash(inter, typ)
// look twice - once without lock, once with.
// common case will be no lock contention.
@@ -56,10 +59,9 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
// was already done once using the , ok form
// and we have a cached negative result.
// the cached result doesn't record which
- // interface function was missing, so jump
- // down to the interface check, which will
- // do more work but give a better error.
- goto search
+ // interface function was missing, so try
+ // adding the itab again, which will throw an error.
+ additab(m, locked != 0, false)
}
}
if locked != 0 {
@@ -73,8 +75,19 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys))
m.inter = inter
m._type = typ
+ additab(m, true, canfail)
+ unlock(&ifaceLock)
+ if m.bad != 0 {
+ return nil
+ }
+ return m
+}
+
+func additab(m *itab, locked, canfail bool) {
+ inter := m.inter
+ typ := m._type
+ x := typ.uncommon()
-search:
// both inter and typ have method sorted by name,
// and interface names are unique,
// so can iterate over both in lock step;
@@ -107,7 +120,7 @@ search:
}
// didn't find method
if !canfail {
- if locked != 0 {
+ if locked {
unlock(&ifaceLock)
}
panic(&TypeAssertionError{"", typ._string, inter.typ._string, iname})
@@ -116,22 +129,22 @@ search:
break
nextimethod:
}
- if locked == 0 {
+ if !locked {
throw("invalid itab locking")
}
+ h := itabhash(inter, typ)
m.link = hash[h]
atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
- unlock(&ifaceLock)
- if m.bad != 0 {
- return nil
- }
- return m
}
-func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
- tab := getitab(inter, t, false)
- atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
- return tab
+func itabsinit() {
+ lock(&ifaceLock)
+ for m := &firstmoduledata; m != nil; m = m.next {
+ for _, i := range m.itablinks {
+ additab(i, true, false)
+ }
+ }
+ unlock(&ifaceLock)
}
func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e eface) {
@@ -157,18 +170,14 @@ func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e eface) {
return
}
-func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) {
+func convT2I(tab *itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) {
+ t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&t)), funcPC(convT2I))
}
if msanenabled {
msanread(elem, t.size)
}
- tab := (*itab)(atomic.Loadp(unsafe.Pointer(cache)))
- if tab == nil {
- tab = getitab(inter, t, false)
- atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
- }
if isDirectIface(t) {
i.tab = tab
typedmemmove(t, unsafe.Pointer(&i.data), elem)
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 523d890a07..f03bf18ebc 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -159,31 +159,6 @@ func writebarrierptr_nostore(dst *uintptr, src uintptr) {
writebarrierptr_nostore1(dst, src)
}
-//go:nosplit
-func writebarrierstring(dst *[2]uintptr, src [2]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
-}
-
-//go:nosplit
-func writebarrierslice(dst *[3]uintptr, src [3]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- dst[2] = src[2]
-}
-
-//go:nosplit
-func writebarrieriface(dst *[2]uintptr, src [2]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
-}
-
-//go:generate go run wbfat_gen.go -- wbfat.go
-//
-// The above line generates multiword write barriers for
-// all the combinations of ptr+scalar up to four words.
-// The implementations are written to wbfat.go.
-
// typedmemmove copies a value of type t to dst from src.
//go:nosplit
func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
diff --git a/src/runtime/memmove_386.s b/src/runtime/memmove_386.s
index d4baf2280a..52b35a6ac7 100644
--- a/src/runtime/memmove_386.s
+++ b/src/runtime/memmove_386.s
@@ -70,24 +70,29 @@ nosse2:
* forward copy loop
*/
forward:
+ // If REP MOVSB isn't fast, don't use it
+ TESTL $(1<<9), runtime·cpuid_ebx7(SB) // erms, aka enhanced REP MOVSB/STOSB
+ JEQ fwdBy4
+
// Check alignment
MOVL SI, AX
ORL DI, AX
TESTL $3, AX
- JNE unaligned_fwd
+ JEQ fwdBy4
+
+ // Do 1 byte at a time
+ MOVL BX, CX
+ REP; MOVSB
+ RET
+fwdBy4:
+ // Do 4 bytes at a time
MOVL BX, CX
SHRL $2, CX
ANDL $3, BX
-
REP; MOVSL
JMP tail
-unaligned_fwd:
- MOVL BX, CX
- REP; MOVSB
- RET
-
/*
* check overlap
*/
diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s
index 514eb169f1..39b4c3a2bb 100644
--- a/src/runtime/memmove_amd64.s
+++ b/src/runtime/memmove_amd64.s
@@ -77,25 +77,29 @@ forward:
CMPQ BX, $2048
JLS move_256through2048
+ // If REP MOVSB isn't fast, don't use it
+ TESTL $(1<<9), runtime·cpuid_ebx7(SB) // erms, aka enhanced REP MOVSB/STOSB
+ JEQ fwdBy8
+
// Check alignment
- MOVQ SI, AX
- ORQ DI, AX
+ MOVL SI, AX
+ ORL DI, AX
TESTL $7, AX
- JNE unaligned_fwd
+ JEQ fwdBy8
+
+ // Do 1 byte at a time
+ MOVQ BX, CX
+ REP; MOVSB
+ RET
- // Aligned - do 8 bytes at a time
+fwdBy8:
+ // Do 8 bytes at a time
MOVQ BX, CX
SHRQ $3, CX
ANDQ $7, BX
REP; MOVSQ
JMP tail
-unaligned_fwd:
- // Unaligned - do 1 byte at a time
- MOVQ BX, CX
- REP; MOVSB
- RET
-
back:
/*
* check overlap
diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go
index d6d91d2021..31d1a80183 100644
--- a/src/runtime/mgcsweep.go
+++ b/src/runtime/mgcsweep.go
@@ -96,7 +96,7 @@ func sweepone() uintptr {
mheap_.sweepdone = 1
_g_.m.locks--
if debug.gcpacertrace > 0 && idx == uint32(len(work.spans)) {
- print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", mheap_.spanBytesAlloc>>20, "MB of spans; swept ", mheap_.pagesSwept, " pages\n")
+ print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", mheap_.spanBytesAlloc>>20, "MB of spans; swept ", mheap_.pagesSwept, " pages at ", mheap_.sweepPagesPerByte, " pages/byte\n")
}
return ^uintptr(0)
}
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index 0f2f0637d2..895af9f07c 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -671,7 +671,7 @@ func (h *mheap) grow(npage uintptr) bool {
}
atomic.Store(&s.sweepgen, h.sweepgen)
s.state = _MSpanInUse
- h.pagesInUse += uint64(npage)
+ h.pagesInUse += uint64(s.npages)
h.freeSpanLocked(s, false, true, 0)
return true
}
diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go
index 1c1ead8790..726dd649fe 100644
--- a/src/runtime/os1_linux.go
+++ b/src/runtime/os1_linux.go
@@ -305,6 +305,7 @@ func memlimit() uintptr {
func sigreturn()
func sigtramp()
+func cgoSigtramp()
//go:nosplit
//go:nowritebarrierrec
@@ -323,7 +324,11 @@ func setsig(i int32, fn uintptr, restart bool) {
sa.sa_restorer = funcPC(sigreturn)
}
if fn == funcPC(sighandler) {
- fn = funcPC(sigtramp)
+ if iscgo {
+ fn = funcPC(cgoSigtramp)
+ } else {
+ fn = funcPC(sigtramp)
+ }
}
sa.sa_handler = fn
rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
@@ -354,7 +359,7 @@ func getsig(i int32) uintptr {
if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
throw("rt_sigaction read failure")
}
- if sa.sa_handler == funcPC(sigtramp) {
+ if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) {
return funcPC(sighandler)
}
return sa.sa_handler
diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go
index 8d46bca36d..315dd9816a 100644
--- a/src/runtime/os1_windows.go
+++ b/src/runtime/os1_windows.go
@@ -41,6 +41,7 @@ import (
//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll"
//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll"
//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
@@ -84,6 +85,7 @@ var (
_SetUnhandledExceptionFilter,
_SetWaitableTimer,
_SuspendThread,
+ _SwitchToThread,
_VirtualAlloc,
_VirtualFree,
_WSAGetOverlappedResult,
@@ -93,8 +95,11 @@ var (
// Following syscalls are only available on some Windows PCs.
// We will load syscalls, if available, before using them.
+ _AddDllDirectory,
_AddVectoredContinueHandler,
- _GetQueuedCompletionStatusEx stdFunction
+ _GetQueuedCompletionStatusEx,
+ _LoadLibraryExW,
+ _ stdFunction
)
type sigset struct{}
@@ -105,21 +110,28 @@ func asmstdcall(fn unsafe.Pointer)
var asmstdcallAddr unsafe.Pointer
+func windowsFindfunc(name []byte, lib uintptr) stdFunction {
+ f := stdcall2(_GetProcAddress, lib, uintptr(unsafe.Pointer(&name[0])))
+ return stdFunction(unsafe.Pointer(f))
+}
+
func loadOptionalSyscalls() {
- var buf [50]byte // large enough for longest string
- strtoptr := func(s string) uintptr {
- buf[copy(buf[:], s)] = 0 // nil-terminated for OS
- return uintptr(noescape(unsafe.Pointer(&buf[0])))
- }
- l := stdcall1(_LoadLibraryA, strtoptr("kernel32.dll"))
- findfunc := func(name string) stdFunction {
- f := stdcall2(_GetProcAddress, l, strtoptr(name))
- return stdFunction(unsafe.Pointer(f))
- }
- if l != 0 {
- _AddVectoredContinueHandler = findfunc("AddVectoredContinueHandler")
- _GetQueuedCompletionStatusEx = findfunc("GetQueuedCompletionStatusEx")
+ var (
+ kernel32dll = []byte("kernel32.dll\000")
+ addVectoredContinueHandler = []byte("AddVectoredContinueHandler\000")
+ getQueuedCompletionStatusEx = []byte("GetQueuedCompletionStatusEx\000")
+ addDllDirectory = []byte("AddDllDirectory\000")
+ loadLibraryExW = []byte("LoadLibraryExW\000")
+ )
+
+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))
+ if k32 == 0 {
+ throw("kernel32.dll not found")
}
+ _AddDllDirectory = windowsFindfunc(addDllDirectory, k32)
+ _AddVectoredContinueHandler = windowsFindfunc(addVectoredContinueHandler, k32)
+ _GetQueuedCompletionStatusEx = windowsFindfunc(getQueuedCompletionStatusEx, k32)
+ _LoadLibraryExW = windowsFindfunc(loadLibraryExW, k32)
}
//go:nosplit
@@ -128,6 +140,11 @@ func getLoadLibrary() uintptr {
}
//go:nosplit
+func getLoadLibraryEx() uintptr {
+ return uintptr(unsafe.Pointer(_LoadLibraryExW))
+}
+
+//go:nosplit
func getGetProcAddress() uintptr {
return uintptr(unsafe.Pointer(_GetProcAddress))
}
@@ -161,13 +178,33 @@ const (
// in sys_windows_386.s and sys_windows_amd64.s
func externalthreadhandler()
+// When loading DLLs, we prefer to use LoadLibraryEx with
+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not
+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*
+// flags are not available on some versions of Windows without a
+// security patch.
+//
+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
+// systems that have KB2533623 installed. To determine whether the
+// flags are available, use GetProcAddress to get the address of the
+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
+// flags can be used with LoadLibraryEx."
+var useLoadLibraryEx bool
+
func osinit() {
asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
+ usleep2Addr = unsafe.Pointer(funcPC(usleep2))
+ switchtothreadAddr = unsafe.Pointer(funcPC(switchtothread))
setBadSignalMsg()
loadOptionalSyscalls()
+ useLoadLibraryEx = (_LoadLibraryExW != nil && _AddDllDirectory != nil)
+
disableWER()
externalthreadhandlerp = funcPC(externalthreadhandler)
@@ -368,8 +405,11 @@ func semacreate(mp *m) {
mp.waitsema = stdcall4(_CreateEventA, 0, 0, 0, 0)
}
-// May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
+// May run with m.p==nil, so write barriers are not allowed. This
+// function is called by newosproc0, so it is also required to
+// operate without stack guards.
+//go:nowritebarrierc
+//go:nosplit
func newosproc(mp *m, stk unsafe.Pointer) {
const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000
thandle := stdcall6(_CreateThread, 0, 0x20000,
@@ -381,6 +421,15 @@ func newosproc(mp *m, stk unsafe.Pointer) {
}
}
+// Used by the C library build mode. On Linux this function would allocate a
+// stack, but that's not necessary for Windows. No stack guards are present
+// and the GC has not been initialized, so write barriers will fail.
+//go:nowritebarrierc
+//go:nosplit
+func newosproc0(mp *m, stk unsafe.Pointer) {
+ newosproc(mp, stk)
+}
+
// Called to initialize a new m (including the bootstrap m).
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
func mpreinit(mp *m) {
@@ -546,17 +595,22 @@ func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
}
// in sys_windows_386.s and sys_windows_amd64.s
-func usleep1(usec uint32)
+func onosstack(fn unsafe.Pointer, arg uint32)
+func usleep2(usec uint32)
+func switchtothread()
+
+var usleep2Addr unsafe.Pointer
+var switchtothreadAddr unsafe.Pointer
//go:nosplit
func osyield() {
- usleep1(1)
+ onosstack(switchtothreadAddr, 0)
}
//go:nosplit
func usleep(us uint32) {
// Have 1us units; want 100ns units.
- usleep1(10 * us)
+ onosstack(usleep2Addr, 10*us)
}
func ctrlhandler1(_type uint32) uint32 {
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index c30ce7a5a3..5145c84aea 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -435,6 +435,7 @@ func schedinit() {
tracebackinit()
moduledataverify()
stackinit()
+ itabsinit()
mallocinit()
mcommoninit(_g_.m)
@@ -509,6 +510,11 @@ func mcommoninit(mp *m) {
// so we need to publish it safely.
atomicstorep(unsafe.Pointer(&allm), unsafe.Pointer(mp))
unlock(&sched.lock)
+
+ // Allocate memory to hold a cgo traceback if the cgo call crashes.
+ if iscgo || GOOS == "solaris" || GOOS == "windows" {
+ mp.cgoCallers = new(cgoCallers)
+ }
}
// Mark gp ready to run.
@@ -715,9 +721,13 @@ func casgstatus(gp *g, oldval, newval uint32) {
throw("casgstatus")
}
+ // See http://golang.org/cl/21503 for justification of the yield delay.
+ const yieldDelay = 5 * 1000
+ var nextYield int64
+
// loop if gp->atomicstatus is in a scan state giving
// GC time to finish and change the state to oldval.
- for !atomic.Cas(&gp.atomicstatus, oldval, newval) {
+ for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ {
if oldval == _Gwaiting && gp.atomicstatus == _Grunnable {
systemstack(func() {
throw("casgstatus: waiting for Gwaiting but is Grunnable")
@@ -730,6 +740,18 @@ func casgstatus(gp *g, oldval, newval uint32) {
// gcphasework(gp)
// })
// }
+ // But meanwhile just yield.
+ if i == 0 {
+ nextYield = nanotime() + yieldDelay
+ }
+ if nanotime() < nextYield {
+ for x := 0; x < 10 && gp.atomicstatus != oldval; x++ {
+ procyield(1)
+ }
+ } else {
+ osyield()
+ nextYield = nanotime() + yieldDelay/2
+ }
}
if newval == _Grunning {
gp.gcscanvalid = false
@@ -767,12 +789,17 @@ func scang(gp *g) {
gp.gcscandone = false
+ // See http://golang.org/cl/21503 for justification of the yield delay.
+ const yieldDelay = 10 * 1000
+ var nextYield int64
+
// Endeavor to get gcscandone set to true,
// either by doing the stack scan ourselves or by coercing gp to scan itself.
// gp.gcscandone can transition from false to true when we're not looking
// (if we asked for preemption), so any time we lock the status using
// castogscanstatus we have to double-check that the scan is still not done.
- for !gp.gcscandone {
+loop:
+ for i := 0; !gp.gcscandone; i++ {
switch s := readgstatus(gp); s {
default:
dumpgstatus(gp)
@@ -781,6 +808,7 @@ func scang(gp *g) {
case _Gdead:
// No stack.
gp.gcscandone = true
+ break loop
case _Gcopystack:
// Stack being switched. Go around again.
@@ -796,6 +824,7 @@ func scang(gp *g) {
gp.gcscandone = true
}
restartg(gp)
+ break loop
}
case _Gscanwaiting:
@@ -821,6 +850,16 @@ func scang(gp *g) {
casfrom_Gscanstatus(gp, _Gscanrunning, _Grunning)
}
}
+
+ if i == 0 {
+ nextYield = nanotime() + yieldDelay
+ }
+ if nanotime() < nextYield {
+ procyield(10)
+ } else {
+ osyield()
+ nextYield = nanotime() + yieldDelay/2
+ }
}
gp.preemptscan = false // cancel scan request if no longer needed
@@ -3993,7 +4032,16 @@ func runqgrab(_p_ *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool
// Instead of stealing runnext in this window, back off
// to give _p_ a chance to schedule runnext. This will avoid
// thrashing gs between different Ps.
- usleep(100)
+ // A sync chan send/recv takes ~50ns as of time of writing,
+ // so 3us gives ~50x overshoot.
+ if GOOS != "windows" {
+ usleep(3)
+ } else {
+ // On windows system timer granularity is 1-15ms,
+ // which is way too much for this optimization.
+ // So just yield.
+ osyield()
+ }
if !_p_.runnext.cas(next, 0) {
continue
}
diff --git a/src/runtime/rt0_windows_386.s b/src/runtime/rt0_windows_386.s
index 0150cc2918..b9407a9879 100644
--- a/src/runtime/rt0_windows_386.s
+++ b/src/runtime/rt0_windows_386.s
@@ -12,5 +12,39 @@ TEXT _rt0_386_windows(SB),NOSPLIT,$12
MOVL $-1, 0(SP) // return PC for main
JMP _main(SB)
+// When building with -buildmode=(c-shared or c-archive), this
+// symbol is called. For dynamic libraries it is called when the
+// library is loaded. For static libraries it is called when the
+// final executable starts, during the C runtime initialization
+// phase.
+TEXT _rt0_386_windows_lib(SB),NOSPLIT,$0x1C
+ MOVL BP, 0x08(SP)
+ MOVL BX, 0x0C(SP)
+ MOVL AX, 0x10(SP)
+ MOVL CX, 0x14(SP)
+ MOVL DX, 0x18(SP)
+
+ // Create a new thread to do the runtime initialization and return.
+ MOVL _cgo_sys_thread_create(SB), AX
+ MOVL $_rt0_386_windows_lib_go(SB), 0x00(SP)
+ MOVL $0, 0x04(SP)
+
+ // Top two items on the stack are passed to _cgo_sys_thread_create
+ // as parameters. This is the calling convention on 32-bit Windows.
+ CALL AX
+
+ MOVL 0x08(SP), BP
+ MOVL 0x0C(SP), BX
+ MOVL 0x10(SP), AX
+ MOVL 0x14(SP), CX
+ MOVL 0x18(SP), DX
+ RET
+
+TEXT _rt0_386_windows_lib_go(SB),NOSPLIT,$0
+ MOVL $0, DI
+ MOVL $0, SI
+ MOVL $runtime·rt0_go(SB), AX
+ JMP AX
+
TEXT _main(SB),NOSPLIT,$0
JMP runtime·rt0_go(SB)
diff --git a/src/runtime/rt0_windows_amd64.s b/src/runtime/rt0_windows_amd64.s
index 95dce06d71..2f73b37f31 100644
--- a/src/runtime/rt0_windows_amd64.s
+++ b/src/runtime/rt0_windows_amd64.s
@@ -12,6 +12,37 @@ TEXT _rt0_amd64_windows(SB),NOSPLIT,$-8
MOVQ $main(SB), AX
JMP AX
+// When building with -buildmode=(c-shared or c-archive), this
+// symbol is called. For dynamic libraries it is called when the
+// library is loaded. For static libraries it is called when the
+// final executable starts, during the C runtime initialization
+// phase.
+TEXT _rt0_amd64_windows_lib(SB),NOSPLIT,$0x28
+ MOVQ BP, 0x00(SP)
+ MOVQ BX, 0x08(SP)
+ MOVQ AX, 0x10(SP)
+ MOVQ CX, 0x18(SP)
+ MOVQ DX, 0x20(SP)
+
+ // Create a new thread to do the runtime initialization and return.
+ MOVQ _cgo_sys_thread_create(SB), AX
+ MOVQ $_rt0_amd64_windows_lib_go(SB), CX
+ MOVQ $0, DX
+ CALL AX
+
+ MOVQ 0x00(SP), BP
+ MOVQ 0x08(SP), BX
+ MOVQ 0x10(SP), AX
+ MOVQ 0x18(SP), CX
+ MOVQ 0x20(SP), DX
+ RET
+
+TEXT _rt0_amd64_windows_lib_go(SB),NOSPLIT,$0
+ MOVQ $0, DI
+ MOVQ $0, SI
+ MOVQ $runtime·rt0_go(SB), AX
+ JMP AX
+
TEXT main(SB),NOSPLIT,$-8
MOVQ $runtime·rt0_go(SB), AX
JMP AX
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index a54dc552c1..e0137f7e97 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -373,8 +373,10 @@ type m struct {
newSigstack bool // minit on C thread called sigaltstack
printlock int8
fastrand uint32
- ncgocall uint64 // number of cgo calls in total
- ncgo int32 // number of cgo calls currently in progress
+ ncgocall uint64 // number of cgo calls in total
+ ncgo int32 // number of cgo calls currently in progress
+ cgoCallersUse uint32 // if non-zero, cgoCallers in use temporarily
+ cgoCallers *cgoCallers // cgo traceback if crashing in cgo call
park note
alllink *m // on allm
schedlink muintptr
@@ -577,6 +579,8 @@ type _func struct {
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
+// Needs to be in sync with
+// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
type itab struct {
inter *interfacetype
_type *_type
@@ -699,6 +703,7 @@ var (
// Set on startup in asm_{x86,amd64}.s.
cpuid_ecx uint32
cpuid_edx uint32
+ cpuid_ebx7 uint32
lfenceBeforeRdtsc bool
support_avx bool
support_avx2 bool
diff --git a/src/runtime/signal_solaris.go b/src/runtime/signal_solaris.go
index 2cab5b8239..a86f7bf6b4 100644
--- a/src/runtime/signal_solaris.go
+++ b/src/runtime/signal_solaris.go
@@ -32,7 +32,7 @@ var sigtable = [...]sigTabT{
/* 19 */ {_SigNotify, "SIGPWR: power-fail restart"},
/* 20 */ {_SigNotify, "SIGWINCH: window size change"},
/* 21 */ {_SigNotify, "SIGURG: urgent socket condition"},
- /* 22 */ {_SigNotify, "SIGPOLL: pollable event occured"},
+ /* 22 */ {_SigNotify, "SIGPOLL: pollable event occurred"},
/* 23 */ {_SigNotify + _SigDefault, "SIGSTOP: stop (cannot be caught or ignored)"},
/* 24 */ {_SigNotify + _SigDefault, "SIGTSTP: user stop requested from tty"},
/* 25 */ {_SigNotify + _SigDefault, "SIGCONT: stopped process has been continued"},
diff --git a/src/runtime/string.go b/src/runtime/string.go
index 3e49b9431e..2d20e0a9c3 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -236,6 +236,9 @@ func intstring(buf *[4]byte, v int64) string {
} else {
s, b = rawstring(4)
}
+ if int64(rune(v)) != v {
+ v = runeerror
+ }
n := runetochar(b, rune(v))
return s[:n]
}
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index cd328eb899..158bdcea0d 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -129,6 +129,7 @@ type moduledata struct {
end, gcdata, gcbss uintptr
typelinks []*_type
+ itablinks []*itab
modulename string
modulehashes []modulehash
diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s
index 3bf5eb0df4..4fe07e0837 100644
--- a/src/runtime/sys_linux_386.s
+++ b/src/runtime/sys_linux_386.s
@@ -232,6 +232,9 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12
CALL runtime·sigtrampgo(SB)
RET
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+ JMP runtime·sigtramp(SB)
+
TEXT runtime·sigreturn(SB),NOSPLIT,$0
MOVL $173, AX // rt_sigreturn
// Sigreturn expects same SP as signal handler,
diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s
index 7cab649238..031e412673 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -234,8 +234,65 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24
CALL AX
RET
+// Used instead of sigtramp in programs that use cgo.
+// Arguments from kernel are in DI, SI, DX.
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+ // If no traceback function, do usual sigtramp.
+ MOVQ runtime·cgoTraceback(SB), AX
+ TESTQ AX, AX
+ JZ sigtramp
+
+ // If no traceback support function, which means that
+ // runtime/cgo was not linked in, do usual sigtramp.
+ MOVQ _cgo_callers(SB), AX
+ TESTQ AX, AX
+ JZ sigtramp
+
+ // Figure out if we are currently in a cgo call.
+ // If not, just do usual sigtramp.
+ get_tls(CX)
+ MOVQ g(CX),AX
+ TESTQ AX, AX
+ JZ sigtramp // g == nil
+ MOVQ g_m(AX), AX
+ TESTQ AX, AX
+ JZ sigtramp // g.m == nil
+ MOVL m_ncgo(AX), CX
+ TESTL CX, CX
+ JZ sigtramp // g.m.ncgo == 0
+ MOVQ m_curg(AX), CX
+ TESTQ CX, CX
+ JZ sigtramp // g.m.curg == nil
+ MOVQ g_syscallsp(CX), CX
+ TESTQ CX, CX
+ JZ sigtramp // g.m.curg.syscallsp == 0
+ MOVQ m_cgoCallers(AX), R8
+ TESTQ R8, R8
+ JZ sigtramp // g.m.cgoCallers == nil
+ MOVL m_cgoCallersUse(AX), CX
+ TESTL CX, CX
+ JNZ sigtramp // g.m.cgoCallersUse != 0
+
+ // Jump to a function in runtime/cgo.
+ // That function, written in C, will call the user's traceback
+ // function with proper unwind info, and will then call back here.
+ // The first three arguments are already in registers.
+ // Set the last three arguments now.
+ MOVQ runtime·cgoTraceback(SB), CX
+ MOVQ $runtime·sigtramp(SB), R9
+ MOVQ _cgo_callers(SB), AX
+ JMP AX
+
+sigtramp:
+ JMP runtime·sigtramp(SB)
+
+// For cgo unwinding to work, this function must look precisely like
+// the one in glibc. The glibc source code is:
+// https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c
+// The code that cares about the precise instructions used is:
+// https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/i386/linux-unwind.h?revision=219188&view=markup
TEXT runtime·sigreturn(SB),NOSPLIT,$0
- MOVL $15, AX // rt_sigreturn
+ MOVQ $15, AX // rt_sigreturn
SYSCALL
INT $3 // not reached
diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s
index 50a551320a..5e5fcf0e6f 100644
--- a/src/runtime/sys_linux_arm.s
+++ b/src/runtime/sys_linux_arm.s
@@ -361,6 +361,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12
BL (R11)
RET
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+ MOVW $runtime·sigtramp(SB), R11
+ B (R11)
+
TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
MOVW sig+0(FP), R0
MOVW new+4(FP), R1
diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s
index 94c101a3d4..1bee8477ed 100644
--- a/src/runtime/sys_linux_arm64.s
+++ b/src/runtime/sys_linux_arm64.s
@@ -259,6 +259,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24
BL (R0)
RET
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+ MOVD $runtime·sigtramp(SB), R3
+ B (R3)
+
TEXT runtime·mmap(SB),NOSPLIT,$-8
MOVD addr+0(FP), R0
MOVD n+8(FP), R1
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
index 26437ddde4..f6877cb32d 100644
--- a/src/runtime/sys_linux_mips64x.s
+++ b/src/runtime/sys_linux_mips64x.s
@@ -249,6 +249,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
JAL (R1)
RET
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+ MOVV $runtime·sigtramp(SB), R1
+ JMP (R1)
+
TEXT runtime·mmap(SB),NOSPLIT,$-8
MOVV addr+0(FP), R4
MOVV n+8(FP), R5
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index d063e025a6..56b842ac01 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -243,6 +243,21 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
MOVD 24(R1), R2
RET
+#ifdef GOARCH_ppc64le
+// ppc64le doesn't need function descriptors
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+#else
+// function descriptor for the real sigtramp
+TEXT runtime·cgoSigtramp(SB),NOSPLIT|NOFRAME,$0
+ DWORD $runtime·_cgoSigtramp(SB)
+ DWORD $0
+ DWORD $0
+TEXT runtime·_cgoSigtramp(SB),NOSPLIT,$0
+#endif
+ MOVD $runtime·sigtramp(SB), R12
+ MOVD R12, CTR
+ JMP (CTR)
+
TEXT runtime·mmap(SB),NOSPLIT|NOFRAME,$0
MOVD addr+0(FP), R3
MOVD n+8(FP), R4
diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s
index 55cdcf407f..95130b733d 100644
--- a/src/runtime/sys_windows_386.s
+++ b/src/runtime/sys_windows_386.s
@@ -358,10 +358,11 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
MOVL CX, 0x14(FS)
RET
-// Sleep duration is in 100ns units.
-TEXT runtime·usleep1(SB),NOSPLIT,$0
- MOVL usec+0(FP), BX
- MOVL $runtime·usleep2(SB), AX // to hide from 8l
+// onosstack calls fn on OS stack.
+// func onosstack(fn unsafe.Pointer, arg uint32)
+TEXT runtime·onosstack(SB),NOSPLIT,$0
+ MOVL fn+0(FP), AX // to hide from 8l
+ MOVL arg+4(FP), BX
// Execute call on m->g0 stack, in case we are not actually
// calling a system call wrapper, like when running under WINE.
@@ -423,6 +424,14 @@ TEXT runtime·usleep2(SB),NOSPLIT,$20
MOVL BP, SP
RET
+// Runs on OS stack.
+TEXT runtime·switchtothread(SB),NOSPLIT,$0
+ MOVL SP, BP
+ MOVL runtime·_SwitchToThread(SB), AX
+ CALL AX
+ MOVL BP, SP
+ RET
+
// func now() (sec int64, nsec int32)
TEXT time·now(SB),NOSPLIT,$8-12
CALL runtime·unixnano(SB)
diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
index caa18e68e9..d550a818ce 100644
--- a/src/runtime/sys_windows_amd64.s
+++ b/src/runtime/sys_windows_amd64.s
@@ -381,10 +381,10 @@ TEXT runtime·settls(SB),NOSPLIT,$0
MOVQ DI, 0x28(GS)
RET
-// Sleep duration is in 100ns units.
-TEXT runtime·usleep1(SB),NOSPLIT,$0
- MOVL usec+0(FP), BX
- MOVQ $runtime·usleep2(SB), AX // to hide from 6l
+// func onosstack(fn unsafe.Pointer, arg uint32)
+TEXT runtime·onosstack(SB),NOSPLIT,$0
+ MOVQ fn+0(FP), AX // to hide from 6l
+ MOVL arg+8(FP), BX
// Execute call on m->g0 stack, in case we are not actually
// calling a system call wrapper, like when running under WINE.
@@ -445,6 +445,18 @@ TEXT runtime·usleep2(SB),NOSPLIT,$48
MOVQ 40(SP), SP
RET
+// Runs on OS stack.
+TEXT runtime·switchtothread(SB),NOSPLIT,$0
+ MOVQ SP, AX
+ ANDQ $~15, SP // alignment as per Windows requirement
+ SUBQ $(48), SP // room for SP and 4 args as per Windows requirement
+ // plus one extra word to keep stack 16 bytes aligned
+ MOVQ AX, 32(SP)
+ MOVQ runtime·_SwitchToThread(SB), AX
+ CALL AX
+ MOVQ 32(SP), SP
+ RET
+
// func now() (sec int64, nsec int32)
TEXT time·now(SB),NOSPLIT,$8-12
CALL runtime·unixnano(SB)
diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go
index ebfa32ff8c..cd23b8da6b 100644
--- a/src/runtime/syscall_windows.go
+++ b/src/runtime/syscall_windows.go
@@ -88,6 +88,41 @@ func compileCallback(fn eface, cleanstack bool) (code uintptr) {
return callbackasmAddr(n)
}
+const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
+
+//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
+//go:nosplit
+func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
+ c := &getg().m.syscall
+
+ if useLoadLibraryEx {
+ c.fn = getLoadLibraryEx()
+ c.n = 3
+ args := struct {
+ lpFileName *uint16
+ hFile uintptr // always 0
+ flags uint32
+ }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32}
+ c.args = uintptr(noescape(unsafe.Pointer(&args)))
+ } else {
+ // User is on Windows XP or something ancient.
+ // The caller wanted to only load the filename DLL
+ // from the System32 directory but that facility
+ // doesn't exist, so just load it the normal way. This
+ // is a potential security risk, but so is Windows XP.
+ c.fn = getLoadLibrary()
+ c.n = 1
+ c.args = uintptr(noescape(unsafe.Pointer(&filename)))
+ }
+
+ cgocall(asmstdcallAddr, unsafe.Pointer(c))
+ handle = c.r1
+ if handle == 0 {
+ err = c.err
+ }
+ return
+}
+
//go:linkname syscall_loadlibrary syscall.loadlibrary
//go:nosplit
func syscall_loadlibrary(filename *uint16) (handle, err uintptr) {
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index f3434d2026..ff045338c1 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -7,6 +7,8 @@ package runtime_test
import (
"bytes"
"fmt"
+ "internal/syscall/windows/sysdll"
+ "internal/testenv"
"io/ioutil"
"os"
"os/exec"
@@ -771,3 +773,246 @@ func TestNumCPU(t *testing.T) {
t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
}
}
+
+// See Issue 14959
+func TestDLLPreloadMitigation(t *testing.T) {
+ if _, err := exec.LookPath("gcc"); err != nil {
+ t.Skip("skipping test: gcc is missing")
+ }
+
+ tmpdir, err := ioutil.TempDir("", "TestDLLPreloadMitigation")
+ if err != nil {
+ t.Fatal("TempDir failed: ", err)
+ }
+ defer func() {
+ err := os.RemoveAll(tmpdir)
+ if err != nil {
+ t.Error(err)
+ }
+ }()
+
+ dir0, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(dir0)
+
+ const src = `
+#include <stdint.h>
+#include <windows.h>
+
+uintptr_t cfunc() {
+ SetLastError(123);
+}
+`
+ srcname := "nojack.c"
+ err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ name := "nojack.dll"
+ cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to build dll: %v - %v", err, string(out))
+ }
+ dllpath := filepath.Join(tmpdir, name)
+
+ dll := syscall.MustLoadDLL(dllpath)
+ dll.MustFindProc("cfunc")
+ dll.Release()
+
+ // Get into the directory with the DLL we'll load by base name
+ // ("nojack.dll") Think of this as the user double-clicking an
+ // installer from their Downloads directory where a browser
+ // silently downloaded some malicious DLLs.
+ os.Chdir(tmpdir)
+
+ // First before we can load a DLL from the current directory,
+ // loading it only as "nojack.dll", without an absolute path.
+ delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
+ dll, err = syscall.LoadDLL(name)
+ if err != nil {
+ t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
+ }
+ dll.Release()
+
+ // And now verify that if we register it as a system32-only
+ // DLL, the implicit loading from the current directory no
+ // longer works.
+ sysdll.IsSystemDLL[name] = true
+ dll, err = syscall.LoadDLL(name)
+ if err == nil {
+ dll.Release()
+ if wantLoadLibraryEx() {
+ t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
+ }
+ t.Skip("insecure load of DLL, but expected")
+ }
+}
+
+// wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests.
+func wantLoadLibraryEx() bool {
+ return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce"
+}
+
+func TestLoadLibraryEx(t *testing.T) {
+ use, have, flags := runtime.LoadLibraryExStatus()
+ if use {
+ return // success.
+ }
+ if wantLoadLibraryEx() {
+ t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)",
+ have, flags)
+ }
+ t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
+ have, flags)
+}
+
+var (
+ modwinmm = syscall.NewLazyDLL("winmm.dll")
+ modkernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+ proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
+ proctimeEndPeriod = modwinmm.NewProc("timeEndPeriod")
+
+ procCreateEvent = modkernel32.NewProc("CreateEventW")
+ procSetEvent = modkernel32.NewProc("SetEvent")
+)
+
+func timeBeginPeriod(period uint32) {
+ syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
+}
+
+func timeEndPeriod(period uint32) {
+ syscall.Syscall(proctimeEndPeriod.Addr(), 1, uintptr(period), 0, 0)
+}
+
+func createEvent() (syscall.Handle, error) {
+ r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
+ if r0 == 0 {
+ return 0, syscall.Errno(e0)
+ }
+ return syscall.Handle(r0), nil
+}
+
+func setEvent(h syscall.Handle) error {
+ r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
+ if r0 == 0 {
+ return syscall.Errno(e0)
+ }
+ return nil
+}
+
+func benchChanToSyscallPing(b *testing.B) {
+ n := b.N
+ ch := make(chan int)
+ event, err := createEvent()
+ if err != nil {
+ b.Fatal(err)
+ }
+ go func() {
+ for i := 0; i < n; i++ {
+ syscall.WaitForSingleObject(event, syscall.INFINITE)
+ ch <- 1
+ }
+ }()
+ for i := 0; i < n; i++ {
+ err := setEvent(event)
+ if err != nil {
+ b.Fatal(err)
+ }
+ <-ch
+ }
+}
+
+func BenchmarkChanToSyscallPing1ms(b *testing.B) {
+ timeBeginPeriod(1)
+ benchChanToSyscallPing(b)
+ timeEndPeriod(1)
+}
+
+func BenchmarkChanToSyscallPing15ms(b *testing.B) {
+ benchChanToSyscallPing(b)
+}
+
+func benchSyscallToSyscallPing(b *testing.B) {
+ n := b.N
+ event1, err := createEvent()
+ if err != nil {
+ b.Fatal(err)
+ }
+ event2, err := createEvent()
+ if err != nil {
+ b.Fatal(err)
+ }
+ go func() {
+ for i := 0; i < n; i++ {
+ syscall.WaitForSingleObject(event1, syscall.INFINITE)
+ err := setEvent(event2)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ }()
+ for i := 0; i < n; i++ {
+ err := setEvent(event1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ syscall.WaitForSingleObject(event2, syscall.INFINITE)
+ }
+}
+
+func BenchmarkSyscallToSyscallPing1ms(b *testing.B) {
+ timeBeginPeriod(1)
+ benchSyscallToSyscallPing(b)
+ timeEndPeriod(1)
+}
+
+func BenchmarkSyscallToSyscallPing15ms(b *testing.B) {
+ benchSyscallToSyscallPing(b)
+}
+
+func benchChanToChanPing(b *testing.B) {
+ n := b.N
+ ch1 := make(chan int)
+ ch2 := make(chan int)
+ go func() {
+ for i := 0; i < n; i++ {
+ <-ch1
+ ch2 <- 1
+ }
+ }()
+ for i := 0; i < n; i++ {
+ ch1 <- 1
+ <-ch2
+ }
+}
+
+func BenchmarkChanToChanPing1ms(b *testing.B) {
+ timeBeginPeriod(1)
+ benchChanToChanPing(b)
+ timeEndPeriod(1)
+}
+
+func BenchmarkChanToChanPing15ms(b *testing.B) {
+ benchChanToChanPing(b)
+}
+
+func benchOsYield(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ runtime.OsYield()
+ }
+}
+
+func BenchmarkOsYield1ms(b *testing.B) {
+ timeBeginPeriod(1)
+ benchOsYield(b)
+ timeEndPeriod(1)
+}
+
+func BenchmarkOsYield15ms(b *testing.B) {
+ benchOsYield(b)
+}
diff --git a/src/runtime/testdata/testprogcgo/threadpanic_windows.c b/src/runtime/testdata/testprogcgo/threadpanic_windows.c
index cf960db53a..6f896634a6 100644
--- a/src/runtime/testdata/testprogcgo/threadpanic_windows.c
+++ b/src/runtime/testdata/testprogcgo/threadpanic_windows.c
@@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <process.h>
#include <stdlib.h>
#include <stdio.h>
void gopanic(void);
-static void*
+static unsigned int
die(void* x)
{
gopanic();
diff --git a/src/runtime/testdata/testprogcgo/traceback.go b/src/runtime/testdata/testprogcgo/traceback.go
new file mode 100644
index 0000000000..bb3e70a44f
--- /dev/null
+++ b/src/runtime/testdata/testprogcgo/traceback.go
@@ -0,0 +1,80 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program will crash.
+// We want the stack trace to include the C functions.
+// We use a fake traceback, and a symbolizer that dumps a string we recognize.
+
+/*
+#cgo CFLAGS: -g -O0
+
+#include <stdint.h>
+
+char *p;
+
+static int f3() {
+ *p = 0;
+ return 0;
+}
+
+static int f2() {
+ return f3();
+}
+
+static int f1() {
+ return f2();
+}
+
+struct cgoTracebackArg {
+ uintptr_t context;
+ uintptr_t* buf;
+ uintptr_t max;
+};
+
+struct cgoSymbolizerArg {
+ uintptr_t pc;
+ const char* file;
+ uintptr_t lineno;
+ const char* func;
+ uintptr_t entry;
+ uintptr_t more;
+ uintptr_t data;
+};
+
+void cgoTraceback(void* parg) {
+ struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
+ arg->buf[0] = 1;
+ arg->buf[1] = 2;
+ arg->buf[2] = 3;
+ arg->buf[3] = 0;
+}
+
+void cgoSymbolizer(void* parg) {
+ struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
+ if (arg->pc != arg->data + 1) {
+ arg->file = "unexpected data";
+ } else {
+ arg->file = "cgo symbolizer";
+ }
+ arg->lineno = arg->data + 1;
+ arg->data++;
+}
+*/
+import "C"
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+func init() {
+ register("CrashTraceback", CrashTraceback)
+}
+
+func CrashTraceback() {
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.cgoTraceback), nil, unsafe.Pointer(C.cgoSymbolizer))
+ C.f1()
+}
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 872b2ef903..16b9278641 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -5,6 +5,7 @@
package runtime
import (
+ "runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
@@ -579,6 +580,22 @@ func tracebacktrap(pc, sp, lr uintptr, gp *g) {
}
func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
+ // If the goroutine is in cgo, and we have a cgo traceback, print that.
+ if iscgo && gp.m != nil && gp.m.ncgo > 0 && gp.syscallsp != 0 && gp.m.cgoCallers != nil && gp.m.cgoCallers[0] != 0 {
+ // Lock cgoCallers so that a signal handler won't
+ // change it, copy the array, reset it, unlock it.
+ // We are locked to the thread and are not running
+ // concurrently with a signal handler.
+ // We just have to stop a signal handler from interrupting
+ // in the middle of our copy.
+ atomic.Store(&gp.m.cgoCallersUse, 1)
+ cgoCallers := *gp.m.cgoCallers
+ gp.m.cgoCallers[0] = 0
+ atomic.Store(&gp.m.cgoCallersUse, 0)
+
+ printCgoTraceback(&cgoCallers)
+ }
+
var n int
if readgstatus(gp)&^_Gscan == _Gsyscall {
// Override registers if blocked in system call.
@@ -739,3 +756,233 @@ func isSystemGoroutine(gp *g) bool {
pc == timerprocPC ||
pc == gcBgMarkWorkerPC
}
+
+// SetCgoTraceback records three C functions to use to gather
+// traceback information from C code and to convert that traceback
+// information into symbolic information. These are used when printing
+// stack traces for a program that uses cgo.
+//
+// The traceback and context functions may be called from a signal
+// handler, and must therefore use only async-signal safe functions.
+// The symbolizer function may be called while the program is
+// crashing, and so must be cautious about using memory. None of the
+// functions may call back into Go.
+//
+// The context function will be called with a single argument, a
+// pointer to a struct:
+//
+// struct {
+// Context uintptr
+// }
+//
+// In C syntax, this struct will be
+//
+// struct {
+// uintptr_t Context;
+// };
+//
+// If the Context field is 0, the context function is being called to
+// record the current traceback context. It should record whatever
+// information is needed about the current point of execution to later
+// produce a stack trace, probably the stack pointer and PC. In this
+// case the context function will be called from C code.
+//
+// If the Context field is not 0, then it is a value returned by a
+// previous call to the context function. This case is called when the
+// context is no longer needed; that is, when the Go code is returning
+// to its C code caller. This permits permits the context function to
+// release any associated resources.
+//
+// While it would be correct for the context function to record a
+// complete a stack trace whenever it is called, and simply copy that
+// out in the traceback function, in a typical program the context
+// function will be called many times without ever recording a
+// traceback for that context. Recording a complete stack trace in a
+// call to the context function is likely to be inefficient.
+//
+// The traceback function will be called with a single argument, a
+// pointer to a struct:
+//
+// struct {
+// Context uintptr
+// Buf *uintptr
+// Max uintptr
+// }
+//
+// In C syntax, this struct will be
+//
+// struct {
+// uintptr_t Context;
+// uintptr_t* Buf;
+// uintptr_t Max;
+// };
+//
+// The Context field will be zero to gather a traceback from the
+// current program execution point. In this case, the traceback
+// function will be called from C code.
+//
+// Otherwise Context will be a value previously returned by a call to
+// the context function. The traceback function should gather a stack
+// trace from that saved point in the program execution. The traceback
+// function may be called from an execution thread other than the one
+// that recorded the context, but only when the context is known to be
+// valid and unchanging. The traceback function may also be called
+// deeper in the call stack on the same thread that recorded the
+// context. The traceback function may be called multiple times with
+// the same Context value; it will usually be appropriate to cache the
+// result, if possible, the first time this is called for a specific
+// context value.
+//
+// Buf is where the traceback information should be stored. It should
+// be PC values, such that Buf[0] is the PC of the caller, Buf[1] is
+// the PC of that function's caller, and so on. Max is the maximum
+// number of entries to store. The function should store a zero to
+// indicate the top of the stack, or that the caller is on a different
+// stack, presumably a Go stack.
+//
+// Unlike runtime.Callers, the PC values returned should, when passed
+// to the symbolizer function, return the file/line of the call
+// instruction. No additional subtraction is required or appropriate.
+//
+// The symbolizer function will be called with a single argument, a
+// pointer to a struct:
+//
+// struct {
+// PC uintptr // program counter to fetch information for
+// File *byte // file name (NUL terminated)
+// Lineno uintptr // line number
+// Func *byte // function name (NUL terminated)
+// Entry uintptr // function entry point
+// More uintptr // set non-zero if more info for this PC
+// Data uintptr // unused by runtime, available for function
+// }
+//
+// In C syntax, this struct will be
+//
+// struct {
+// uintptr_t PC;
+// char* File;
+// uintptr_t Lineno;
+// char* Func;
+// uintptr_t Entry;
+// uintptr_t More;
+// uintptr_t Data;
+// };
+//
+// The PC field will be a value returned by a call to the traceback
+// function.
+//
+// The first time the function is called for a particular traceback,
+// all the fields except PC will be 0. The function should fill in the
+// other fields if possible, setting them to 0/nil if the information
+// is not available. The Data field may be used to store any useful
+// information across calls. The More field should be set to non-zero
+// if there is more information for this PC, zero otherwise. If More
+// is set non-zero, the function will be called again with the same
+// PC, and may return different information (this is intended for use
+// with inlined functions). If More is zero, the function will be
+// called with the next PC value in the traceback. When the traceback
+// is complete, the function will be called once more with PC set to
+// zero; this may be used to free any information. Each call will
+// leave the fields of the struct set to the same values they had upon
+// return, except for the PC field when the More field is zero. The
+// function must not keep a copy of the struct pointer between calls.
+//
+// When calling SetCgoTraceback, the version argument is the version
+// number of the structs that the functions expect to receive.
+// Currently this must be zero.
+//
+// The symbolizer function may be nil, in which case the results of
+// the traceback function will be displayed as numbers. If the
+// traceback function is nil, the symbolizer function will never be
+// called. The context function may be nil, in which case the
+// traceback function will only be called with the context field set
+// to zero. If the context function is nil, then calls from Go to C
+// to Go will not show a traceback for the C portion of the call stack.
+func SetCgoTraceback(version int, traceback, context, symbolizer unsafe.Pointer) {
+ if version != 0 {
+ panic("unsupported version")
+ }
+ if context != nil {
+ panic("SetCgoTraceback: context function not yet implemented")
+ }
+ cgoTraceback = traceback
+ cgoContext = context
+ cgoSymbolizer = symbolizer
+}
+
+var cgoTraceback unsafe.Pointer
+var cgoContext unsafe.Pointer
+var cgoSymbolizer unsafe.Pointer
+
+// cgoTracebackArg is the type passed to cgoTraceback.
+type cgoTracebackArg struct {
+ context uintptr
+ buf *uintptr
+ max uintptr
+}
+
+// cgoContextArg is the type passed to cgoContext.
+type cgoContextArg struct {
+ context uintptr
+}
+
+// cgoSymbolizerArg is the type passed to cgoSymbolizer.
+type cgoSymbolizerArg struct {
+ pc uintptr
+ file *byte
+ lineno uintptr
+ funcName *byte
+ entry uintptr
+ more uintptr
+ data uintptr
+}
+
+// cgoTraceback prints a traceback of callers.
+func printCgoTraceback(callers *cgoCallers) {
+ if cgoSymbolizer == nil {
+ for _, c := range callers {
+ if c == 0 {
+ break
+ }
+ print("non-Go function at pc=", hex(c), "\n")
+ }
+ return
+ }
+
+ call := cgocall
+ if panicking > 0 {
+ // We do not want to call into the scheduler when panicking.
+ call = asmcgocall
+ }
+
+ var arg cgoSymbolizerArg
+ for _, c := range callers {
+ if c == 0 {
+ break
+ }
+ arg.pc = c
+ for {
+ call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
+ if arg.funcName != nil {
+ // Note that we don't print any argument
+ // information here, not even parentheses.
+ // The symbolizer must add that if
+ // appropriate.
+ println(gostringnocopy(arg.funcName))
+ } else {
+ println("non-Go function")
+ }
+ print("\t")
+ if arg.file != nil {
+ print(gostringnocopy(arg.file), ":", arg.lineno, " ")
+ }
+ print("pc=", hex(c), "\n")
+ if arg.more == 0 {
+ break
+ }
+ }
+ }
+ arg.pc = 0
+ call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
+}
diff --git a/src/runtime/wbfat.go b/src/runtime/wbfat.go
deleted file mode 100644
index 8fe2cefd8d..0000000000
--- a/src/runtime/wbfat.go
+++ /dev/null
@@ -1,190 +0,0 @@
-// generated by wbfat_gen.go; use go generate
-
-package runtime
-
-//go:nosplit
-func writebarrierfat01(dst *[2]uintptr, _ uintptr, src [2]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
-}
-
-//go:nosplit
-func writebarrierfat10(dst *[2]uintptr, _ uintptr, src [2]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
-}
-
-//go:nosplit
-func writebarrierfat11(dst *[2]uintptr, _ uintptr, src [2]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
-}
-
-//go:nosplit
-func writebarrierfat001(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- dst[0] = src[0]
- dst[1] = src[1]
- writebarrierptr(&dst[2], src[2])
-}
-
-//go:nosplit
-func writebarrierfat010(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
- dst[2] = src[2]
-}
-
-//go:nosplit
-func writebarrierfat011(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
- writebarrierptr(&dst[2], src[2])
-}
-
-//go:nosplit
-func writebarrierfat100(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- dst[2] = src[2]
-}
-
-//go:nosplit
-func writebarrierfat101(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- writebarrierptr(&dst[2], src[2])
-}
-
-//go:nosplit
-func writebarrierfat110(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
- dst[2] = src[2]
-}
-
-//go:nosplit
-func writebarrierfat111(dst *[3]uintptr, _ uintptr, src [3]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
- writebarrierptr(&dst[2], src[2])
-}
-
-//go:nosplit
-func writebarrierfat0001(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- dst[1] = src[1]
- dst[2] = src[2]
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat0010(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- dst[1] = src[1]
- writebarrierptr(&dst[2], src[2])
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat0011(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- dst[1] = src[1]
- writebarrierptr(&dst[2], src[2])
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat0100(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
- dst[2] = src[2]
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat0101(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
- dst[2] = src[2]
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat0110(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
- writebarrierptr(&dst[2], src[2])
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat0111(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- dst[0] = src[0]
- writebarrierptr(&dst[1], src[1])
- writebarrierptr(&dst[2], src[2])
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat1000(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- dst[2] = src[2]
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat1001(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- dst[2] = src[2]
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat1010(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- writebarrierptr(&dst[2], src[2])
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat1011(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- dst[1] = src[1]
- writebarrierptr(&dst[2], src[2])
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat1100(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
- dst[2] = src[2]
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat1101(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
- dst[2] = src[2]
- writebarrierptr(&dst[3], src[3])
-}
-
-//go:nosplit
-func writebarrierfat1110(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
- writebarrierptr(&dst[2], src[2])
- dst[3] = src[3]
-}
-
-//go:nosplit
-func writebarrierfat1111(dst *[4]uintptr, _ uintptr, src [4]uintptr) {
- writebarrierptr(&dst[0], src[0])
- writebarrierptr(&dst[1], src[1])
- writebarrierptr(&dst[2], src[2])
- writebarrierptr(&dst[3], src[3])
-}
diff --git a/src/runtime/wbfat_gen.go b/src/runtime/wbfat_gen.go
deleted file mode 100644
index cac19f5804..0000000000
--- a/src/runtime/wbfat_gen.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "os"
-)
-
-func main() {
- flag.Parse()
- if flag.NArg() > 0 {
- f, err := os.Create(flag.Arg(0))
- if err != nil {
- log.Fatal(err)
- }
- os.Stdout = f
- }
- fmt.Printf("// generated by wbfat_gen.go; use go generate\n\n")
- fmt.Printf("package runtime\n")
- for i := uint(2); i <= 4; i++ {
- for j := 1; j < 1<<i; j++ {
- fmt.Printf("\n//go:nosplit\n")
- fmt.Printf("func writebarrierfat%0*b(dst *[%d]uintptr, _ uintptr, src [%d]uintptr) {\n", int(i), j, i, i)
- for k := uint(0); k < i; k++ {
- if j&(1<<(i-1-k)) != 0 {
- fmt.Printf("\twritebarrierptr(&dst[%d], src[%d])\n", k, k)
- } else {
- fmt.Printf("\tdst[%d] = src[%d]\n", k, k)
- }
- }
- fmt.Printf("}\n")
- }
- }
-}