From ccd992355df7192993c666236047820244914598 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 16 Apr 2024 21:19:13 +0200 Subject: Adding upstream version 1.21.8. Signed-off-by: Daniel Baumann --- src/runtime/runtime-seh_windows_test.go | 191 ++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 src/runtime/runtime-seh_windows_test.go (limited to 'src/runtime/runtime-seh_windows_test.go') diff --git a/src/runtime/runtime-seh_windows_test.go b/src/runtime/runtime-seh_windows_test.go new file mode 100644 index 0000000..27e4f49 --- /dev/null +++ b/src/runtime/runtime-seh_windows_test.go @@ -0,0 +1,191 @@ +// Copyright 2023 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 runtime_test + +import ( + "internal/abi" + "internal/syscall/windows" + "runtime" + "slices" + "testing" + "unsafe" +) + +func sehf1() int { + return sehf1() +} + +func sehf2() {} + +func TestSehLookupFunctionEntry(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("skipping amd64-only test") + } + // This test checks that Win32 is able to retrieve + // function metadata stored in the .pdata section + // by the Go linker. + // Win32 unwinding will fail if this test fails, + // as RtlUnwindEx uses RtlLookupFunctionEntry internally. + // If that's the case, don't bother investigating further, + // first fix the .pdata generation. + sehf1pc := abi.FuncPCABIInternal(sehf1) + var fnwithframe func() + fnwithframe = func() { + fnwithframe() + } + fnwithoutframe := func() {} + tests := []struct { + name string + pc uintptr + hasframe bool + }{ + {"no frame func", abi.FuncPCABIInternal(sehf2), false}, + {"no func", sehf1pc - 1, false}, + {"func at entry", sehf1pc, true}, + {"func in prologue", sehf1pc + 1, true}, + {"anonymous func with frame", abi.FuncPCABIInternal(fnwithframe), true}, + {"anonymous func without frame", abi.FuncPCABIInternal(fnwithoutframe), false}, + {"pc at func body", runtime.NewContextStub().GetPC(), true}, + } + for _, tt := range tests { + var base uintptr + fn := windows.RtlLookupFunctionEntry(tt.pc, &base, nil) + if !tt.hasframe { + if fn != 0 { + t.Errorf("%s: unexpected frame", tt.name) + } + continue + } + if fn == 0 { + t.Errorf("%s: missing frame", tt.name) + } + } +} + +func sehCallers() []uintptr { + // We don't need a real context, + // RtlVirtualUnwind just needs a context with + // valid a pc, sp and fp (aka bp). + ctx := runtime.NewContextStub() + + pcs := make([]uintptr, 15) + var base, frame uintptr + var n int + for i := 0; i < len(pcs); i++ { + fn := windows.RtlLookupFunctionEntry(ctx.GetPC(), &base, nil) + if fn == 0 { + break + } + pcs[i] = ctx.GetPC() + n++ + windows.RtlVirtualUnwind(0, base, ctx.GetPC(), fn, uintptr(unsafe.Pointer(ctx)), nil, &frame, nil) + } + return pcs[:n] +} + +// SEH unwinding does not report inlined frames. +// +//go:noinline +func sehf3(pan bool) []uintptr { + return sehf4(pan) +} + +//go:noinline +func sehf4(pan bool) []uintptr { + var pcs []uintptr + if pan { + panic("sehf4") + } + pcs = sehCallers() + return pcs +} + +func testSehCallersEqual(t *testing.T, pcs []uintptr, want []string) { + t.Helper() + got := make([]string, 0, len(want)) + for _, pc := range pcs { + fn := runtime.FuncForPC(pc) + if fn == nil || len(got) >= len(want) { + break + } + name := fn.Name() + switch name { + case "runtime.deferCallSave", "runtime.runOpenDeferFrame", "runtime.panicmem": + // These functions are skipped as they appear inconsistently depending + // whether inlining is on or off. + continue + } + got = append(got, name) + } + if !slices.Equal(want, got) { + t.Fatalf("wanted %v, got %v", want, got) + } +} + +func TestSehUnwind(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("skipping amd64-only test") + } + pcs := sehf3(false) + testSehCallersEqual(t, pcs, []string{"runtime_test.sehCallers", "runtime_test.sehf4", + "runtime_test.sehf3", "runtime_test.TestSehUnwind"}) +} + +func TestSehUnwindPanic(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("skipping amd64-only test") + } + want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindPanic.func1", "runtime.gopanic", + "runtime_test.sehf4", "runtime_test.sehf3", "runtime_test.TestSehUnwindPanic"} + defer func() { + if r := recover(); r == nil { + t.Fatal("did not panic") + } + pcs := sehCallers() + testSehCallersEqual(t, pcs, want) + }() + sehf3(true) +} + +func TestSehUnwindDoublePanic(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("skipping amd64-only test") + } + want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindDoublePanic.func1.1", "runtime.gopanic", + "runtime_test.TestSehUnwindDoublePanic.func1", "runtime.gopanic", "runtime_test.TestSehUnwindDoublePanic"} + defer func() { + defer func() { + if recover() == nil { + t.Fatal("did not panic") + } + pcs := sehCallers() + testSehCallersEqual(t, pcs, want) + }() + if recover() == nil { + t.Fatal("did not panic") + } + panic(2) + }() + panic(1) +} + +func TestSehUnwindNilPointerPanic(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("skipping amd64-only test") + } + want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindNilPointerPanic.func1", "runtime.gopanic", + "runtime.sigpanic", "runtime_test.TestSehUnwindNilPointerPanic"} + defer func() { + if r := recover(); r == nil { + t.Fatal("did not panic") + } + pcs := sehCallers() + testSehCallersEqual(t, pcs, want) + }() + var p *int + if *p == 3 { + t.Fatal("did not see nil pointer panic") + } +} -- cgit v1.2.3