diff options
Diffstat (limited to 'src/cmd/cgo/internal')
44 files changed, 611 insertions, 101 deletions
diff --git a/src/cmd/cgo/internal/test/callback_windows.go b/src/cmd/cgo/internal/test/callback_windows.go new file mode 100644 index 0000000000..77bdfa4dd3 --- /dev/null +++ b/src/cmd/cgo/internal/test/callback_windows.go @@ -0,0 +1,109 @@ +// 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 cgotest + +/* +#include <windows.h> +USHORT backtrace(ULONG FramesToCapture, PVOID *BackTrace) { +#ifdef _AMD64_ + CONTEXT context; + RtlCaptureContext(&context); + ULONG64 ControlPc; + ControlPc = context.Rip; + int i; + for (i = 0; i < FramesToCapture; i++) { + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 ImageBase; + VOID *HandlerData; + ULONG64 EstablisherFrame; + + FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); + + if (!FunctionEntry) { + // For simplicity, don't unwind leaf entries, which are not used in this test. + break; + } else { + RtlVirtualUnwind(0, ImageBase, ControlPc, FunctionEntry, &context, &HandlerData, &EstablisherFrame, NULL); + } + + ControlPc = context.Rip; + // Check if we left the user range. + if (ControlPc < 0x10000) { + break; + } + + BackTrace[i] = (PVOID)(ControlPc); + } + return i; +#else + return 0; +#endif +} +*/ +import "C" + +import ( + "internal/testenv" + "reflect" + "runtime" + "strings" + "testing" + "unsafe" +) + +// Test that the stack can be unwound through a call out and call back +// into Go. +func testCallbackCallersSEH(t *testing.T) { + testenv.SkipIfOptimizationOff(t) // This test requires inlining. + if runtime.Compiler != "gc" { + // The exact function names are not going to be the same. + t.Skip("skipping for non-gc toolchain") + } + if runtime.GOARCH != "amd64" { + // TODO: support SEH on other architectures. + t.Skip("skipping on non-amd64") + } + // Only frames in the test package are checked. + want := []string{ + "test._Cfunc_backtrace", + "test.testCallbackCallersSEH.func1.1", + "test.testCallbackCallersSEH.func1", + "test.goCallback", + "test._Cfunc_callback", + "test.nestedCall.func1", + "test.nestedCall", + "test.testCallbackCallersSEH", + "test.TestCallbackCallersSEH", + } + pc := make([]uintptr, 100) + n := 0 + nestedCall(func() { + n = int(C.backtrace(C.DWORD(len(pc)), (*C.PVOID)(unsafe.Pointer(&pc[0])))) + }) + got := make([]string, 0, n) + for i := 0; i < n; i++ { + f := runtime.FuncForPC(pc[i] - 1) + if f == nil { + continue + } + fname := f.Name() + switch fname { + case "goCallback": + // TODO(qmuntal): investigate why this function doesn't appear + // when using the external linker. + continue + } + // In module mode, this package has a fully-qualified import path. + // Remove it if present. + fname = strings.TrimPrefix(fname, "cmd/cgo/internal/") + if !strings.HasPrefix(fname, "test.") { + continue + } + got = append(got, fname) + } + if !reflect.DeepEqual(want, got) { + t.Errorf("incorrect backtrace:\nwant:\t%v\ngot:\t%v", want, got) + } +} diff --git a/src/cmd/cgo/internal/test/callstub_linux_ppc64le.go b/src/cmd/cgo/internal/test/callstub_linux_ppc64le.go new file mode 100644 index 0000000000..93c29e1c7c --- /dev/null +++ b/src/cmd/cgo/internal/test/callstub_linux_ppc64le.go @@ -0,0 +1,20 @@ +// 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 cgotest + +// extern int notoc_func(void); +// int TestPPC64Stubs(void) { +// return notoc_func(); +// } +import "C" +import "testing" + +func testPPC64CallStubs(t *testing.T) { + // Verify the trampolines run on the testing machine. If they + // do not, or are missing, a crash is expected. + if C.TestPPC64Stubs() != 0 { + t.Skipf("This test requires binutils 2.35 or newer.") + } +} diff --git a/src/cmd/cgo/internal/test/issue1435.go b/src/cmd/cgo/internal/test/issue1435.go index a672e26aa0..1588d39ea9 100644 --- a/src/cmd/cgo/internal/test/issue1435.go +++ b/src/cmd/cgo/internal/test/issue1435.go @@ -8,6 +8,7 @@ package cgotest import ( "fmt" + "internal/testenv" "os" "runtime" "sort" @@ -145,6 +146,13 @@ func test1435(t *testing.T) { if syscall.Getuid() != 0 { t.Skip("skipping root only test") } + if testing.Short() && testenv.Builder() != "" && os.Getenv("USER") == "swarming" { + // The Go build system's swarming user is known not to be root. + // Unfortunately, it sometimes appears as root due the current + // implementation of a no-network check using 'unshare -n -r'. + // Since this test does need root to work, we need to skip it. + t.Skip("skipping root only test on a non-root builder") + } if runtime.GOOS == "linux" { if _, err := os.Stat("/etc/alpine-release"); err == nil { t.Skip("skipping failing test on alpine - go.dev/issue/19938") diff --git a/src/cmd/cgo/internal/test/issue18146.go b/src/cmd/cgo/internal/test/issue18146.go index d302bd029f..112b7ee2e7 100644 --- a/src/cmd/cgo/internal/test/issue18146.go +++ b/src/cmd/cgo/internal/test/issue18146.go @@ -72,7 +72,7 @@ func test18146(t *testing.T) { }() } runtime.GOMAXPROCS(threads) - argv := append(os.Args, "-test.run=NoSuchTestExists") + argv := append(os.Args, "-test.run=^$") if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil { t.Fatal(err) } @@ -85,7 +85,7 @@ func test18146(t *testing.T) { } }() - args := append(append([]string(nil), os.Args[1:]...), "-test.run=Test18146") + args := append(append([]string(nil), os.Args[1:]...), "-test.run=^Test18146$") for n := attempts; n > 0; n-- { cmd := exec.Command(os.Args[0], args...) cmd.Env = append(os.Environ(), "test18146=exec") diff --git a/src/cmd/cgo/internal/test/issue4029.c b/src/cmd/cgo/internal/test/issue4029.c index 212d6922f8..7a8fdc11b4 100644 --- a/src/cmd/cgo/internal/test/issue4029.c +++ b/src/cmd/cgo/internal/test/issue4029.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows && !static && (!darwin || (!internal_pie && !arm64)) +//go:build !windows && !static && !(darwin && internal) #include <stdint.h> #include <dlfcn.h> diff --git a/src/cmd/cgo/internal/test/issue4029.go b/src/cmd/cgo/internal/test/issue4029.go index 686b7679f3..506c999bdb 100644 --- a/src/cmd/cgo/internal/test/issue4029.go +++ b/src/cmd/cgo/internal/test/issue4029.go @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows && !static && (!darwin || (!internal_pie && !arm64)) +//go:build !windows && !static && !(darwin && internal) -// Excluded in darwin internal linking PIE mode, as dynamic export is not -// supported. -// Excluded in internal linking mode on darwin/arm64, as it is always PIE. +// Excluded in darwin internal linking PIE (which is the default) mode, +// as dynamic export is not supported. package cgotest diff --git a/src/cmd/cgo/internal/test/issue4029w.go b/src/cmd/cgo/internal/test/issue4029w.go index 91dad6abcb..aa4c2f59bd 100644 --- a/src/cmd/cgo/internal/test/issue4029w.go +++ b/src/cmd/cgo/internal/test/issue4029w.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build windows || static || (darwin && internal_pie) || (darwin && arm64) +//go:build windows || static || (darwin && internal) package cgotest diff --git a/src/cmd/cgo/internal/test/issue4339.c b/src/cmd/cgo/internal/test/issue4339.c index 15d0004078..d0e64878d1 100644 --- a/src/cmd/cgo/internal/test/issue4339.c +++ b/src/cmd/cgo/internal/test/issue4339.c @@ -1,3 +1,7 @@ +// Copyright 2013 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. + #include <stdio.h> #include "issue4339.h" diff --git a/src/cmd/cgo/internal/test/issue4339.h b/src/cmd/cgo/internal/test/issue4339.h index 20f6cebb6b..99a09960e2 100644 --- a/src/cmd/cgo/internal/test/issue4339.h +++ b/src/cmd/cgo/internal/test/issue4339.h @@ -1,3 +1,7 @@ +// Copyright 2013 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. + typedef struct Issue4339 Issue4339; struct Issue4339 { diff --git a/src/cmd/cgo/internal/test/issue8756.go b/src/cmd/cgo/internal/test/issue8756.go index 817f449e96..d8eadfde6d 100644 --- a/src/cmd/cgo/internal/test/issue8756.go +++ b/src/cmd/cgo/internal/test/issue8756.go @@ -1,3 +1,7 @@ +// 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 cgotest /* diff --git a/src/cmd/cgo/internal/test/issue8756/issue8756.go b/src/cmd/cgo/internal/test/issue8756/issue8756.go index 223397f067..02a1424b9f 100644 --- a/src/cmd/cgo/internal/test/issue8756/issue8756.go +++ b/src/cmd/cgo/internal/test/issue8756/issue8756.go @@ -1,3 +1,7 @@ +// 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 issue8756 /* diff --git a/src/cmd/cgo/internal/test/issue8828/trivial.go b/src/cmd/cgo/internal/test/issue8828/trivial.go index e7b9a4e573..9f2619654f 100644 --- a/src/cmd/cgo/internal/test/issue8828/trivial.go +++ b/src/cmd/cgo/internal/test/issue8828/trivial.go @@ -1,3 +1,7 @@ +// 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. + package issue8828 //void foo(); diff --git a/src/cmd/cgo/internal/test/issue9026/issue9026.go b/src/cmd/cgo/internal/test/issue9026/issue9026.go index ff269ca9eb..13bc180321 100644 --- a/src/cmd/cgo/internal/test/issue9026/issue9026.go +++ b/src/cmd/cgo/internal/test/issue9026/issue9026.go @@ -1,3 +1,7 @@ +// 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. + package issue9026 // This file appears in its own package since the assertion tests the diff --git a/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s b/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s index 1f492eafe9..3edba3dd82 100644 --- a/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s +++ b/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// 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. diff --git a/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s b/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s index fa34f6bd37..0f10e3a326 100644 --- a/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s +++ b/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s @@ -1,4 +1,4 @@ -// Copyright 2020 The Go Authors. All rights reserved. +// Copyright 2020 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. diff --git a/src/cmd/cgo/internal/test/issue9400_linux.go b/src/cmd/cgo/internal/test/issue9400_linux.go index 1511e25876..41b9ab9dc7 100644 --- a/src/cmd/cgo/internal/test/issue9400_linux.go +++ b/src/cmd/cgo/internal/test/issue9400_linux.go @@ -48,7 +48,7 @@ func test9400(t *testing.T) { } // Disable GC for the duration of the test. - // This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695. + // This avoids a potential GC deadlock when spinning in uninterruptible ASM below #49695. defer debug.SetGCPercent(debug.SetGCPercent(-1)) // SetGCPercent waits until the mark phase is over, but the runtime // also preempts at the start of the sweep phase, so make sure that's diff --git a/src/cmd/cgo/internal/test/issue9510a/a.go b/src/cmd/cgo/internal/test/issue9510a/a.go index 1a5224b8c6..f0a0128d10 100644 --- a/src/cmd/cgo/internal/test/issue9510a/a.go +++ b/src/cmd/cgo/internal/test/issue9510a/a.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 issue9510a /* diff --git a/src/cmd/cgo/internal/test/issue9510b/b.go b/src/cmd/cgo/internal/test/issue9510b/b.go index 5016b39597..6e22508c32 100644 --- a/src/cmd/cgo/internal/test/issue9510b/b.go +++ b/src/cmd/cgo/internal/test/issue9510b/b.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 issue9510b /* diff --git a/src/cmd/cgo/internal/test/linux_ppc64le_test.go b/src/cmd/cgo/internal/test/linux_ppc64le_test.go new file mode 100644 index 0000000000..67b6b161d6 --- /dev/null +++ b/src/cmd/cgo/internal/test/linux_ppc64le_test.go @@ -0,0 +1,13 @@ +// 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. + +//go:build ppc64le && linux && cgo + +package cgotest + +import "testing" + +func TestPPC64CallStubs(t *testing.T) { + testPPC64CallStubs(t) +} diff --git a/src/cmd/cgo/internal/test/seh_internal_windows_test.go b/src/cmd/cgo/internal/test/seh_internal_windows_test.go new file mode 100644 index 0000000000..708ffdc6f6 --- /dev/null +++ b/src/cmd/cgo/internal/test/seh_internal_windows_test.go @@ -0,0 +1,16 @@ +// Copyright 2024 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. + +//go:build cgo && windows && internal + +package cgotest + +import ( + "internal/testenv" + "testing" +) + +func TestCallbackCallersSEH(t *testing.T) { + testenv.SkipFlaky(t, 65116) +} diff --git a/src/cmd/cgo/internal/test/seh_windows_test.go b/src/cmd/cgo/internal/test/seh_windows_test.go new file mode 100644 index 0000000000..4a8d5bbd4d --- /dev/null +++ b/src/cmd/cgo/internal/test/seh_windows_test.go @@ -0,0 +1,11 @@ +// 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. + +//go:build cgo && windows && !internal + +package cgotest + +import "testing" + +func TestCallbackCallersSEH(t *testing.T) { testCallbackCallersSEH(t) } diff --git a/src/cmd/cgo/internal/test/stubtest_linux_ppc64le.S b/src/cmd/cgo/internal/test/stubtest_linux_ppc64le.S new file mode 100644 index 0000000000..0c519705a5 --- /dev/null +++ b/src/cmd/cgo/internal/test/stubtest_linux_ppc64le.S @@ -0,0 +1,122 @@ +// 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. + +// When linking C ELFv2 objects, the Go linker may need to insert calling stubs. +// A call stub is usually needed when the ELFv2 st_other attribute is different +// between caller and callee. +// +// The type of call stub inserted will vary depending on GOPPC64 and the +// buildmode (e.g pie builds shared code, default builds fixed-position code). +// CI is set up to run for P8 and P10 machines, and this test is run in both +// pie and default modes. +// +// Several functions are written with interesting st_other attributes, and +// call each other to test various calling combinations which require stubs. +// +// The call tree is as follows, starting from TestPPC64Stubs (A C function): +// TestPPC64Stubs (compiled PIC by default by Go) +// notoc_func [called TOC -> NOTOC (but R2 is preserved)] +// toc_func [called NOTOC -> TOC] +// notoc_nor2_func [called TOC -> NOTOC] +// random [dynamic TOC call] +// random [dynamic NOTOC call] +// +// Depending on the GOPPC64/buildmode used, and type of call, one of 7 stubs may need inserted: +// +// TOC -> NOTOC: Save R2, call global entry. (valid for any GOPPC64) +// TOC save slot is rewrittent to restore TOC. +// NOTOC -> TOC [P10]: A PIC call stub using P10 instructions to call the global entry +// NOTOC -> TOC [P8]: A PIC call stub using P8 instructions to call the global entry +// +// TOC -> dynamic: A PLT call stub is generated which saves R2. +// TOC save slot is rewritten to restore TOC. +// NOTOC -> dynamic [P10]: A stub using pcrel instructions is generated. +// NOTOC -> dynamic [P8/default]: A P8 compatible, non-PIC stub is generated +// NOTOC -> dynamic [P8/pie]: A P8 compatible, PIC stub is generated +// +// +// Some notes about other cases: +// TOC -> TOC, NOTOC -> NOTOC, NOTOC -> TOC local calls do not require require call stubs. +// TOC -> NOTOC (R2 is preserved, st_other==0): A special case where a call stub is not needed. + +// This test requires a binutils with power10 and ELFv2 1.5 support. This is earliest verified version. +.if .gasversion. >= 23500 + +// A function which does not guarantee R2 is preserved. +// R2 is clobbered here to ensure the stubs preserve it. + .globl notoc_nor2_func + .type notoc_nor2_func, @function +notoc_nor2_func: + .localentry notoc_nor2_func,1 + li 2,0 + blr + +// A function which expects R2 to hold TOC, and has a distinct local entry. + .globl toc_func + .type toc_func, @function +toc_func: + addis 2,12,.TOC.-toc_func@ha + addi 2,2,.TOC.-toc_func@l + .localentry toc_func, .-toc_func + mflr 0 + std 0,16(1) + stdu 1,-32(1) + + // Call a NOTOC function which clobbers R2. + bl notoc_nor2_func + nop + + // Call libc random. This should generate a TOC relative plt stub. + bl random + nop + + addi 1,1,32 + ld 0,16(1) + mtlr 0 + blr + +// An ELFv2 st_other==0 function. It preserves R2 (TOC), but does not use it. + .globl notoc_func + .type notoc_func, @function +notoc_func: + // Save R2 and LR and stack a frame. + mflr 0 + std 0,16(1) + stdu 1,-32(1) + + // Save R2 in TOC save slot. + std 2,24(1) + + // clobber R2 + li 2,0 + + // Call type2_func. A call stub from notoc to toc should be inserted. + bl toc_func@notoc + + // Call libc random. A notoc plt stub should be inserted. + bl random@notoc + + // Return 0 to indicate the test ran. + li 3,0 + + // Restore R2 + ld 2,24(1) + + // Restore LR and pop stack + addi 1,1,32 + ld 0,16(1) + mtlr 0 + blr + +.else + +// A stub for older binutils + .globl notoc_func + .type notoc_func, @function +notoc_func: + // Return 1 to indicate the test was skipped. + li 3,1 + blr + +.endif diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go index 7da5a856b3..9b3790eb11 100644 --- a/src/cmd/cgo/internal/test/test.go +++ b/src/cmd/cgo/internal/test/test.go @@ -115,6 +115,14 @@ int add(int x, int y) { return x+y; }; +// escape vs noescape + +// TODO(#56378): enable in Go 1.23: +// #cgo noescape handleGoStringPointerNoescape +void handleGoStringPointerNoescape(void *s) {} + +void handleGoStringPointerEscape(void *s) {} + // Following mimics vulkan complex definitions for benchmarking cgocheck overhead. typedef uint32_t VkFlags; @@ -1106,6 +1114,18 @@ func benchCgoCall(b *testing.B) { C.handleComplexPointer(&a0) } }) + b.Run("string-pointer-escape", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var s string + C.handleGoStringPointerEscape(unsafe.Pointer(&s)) + } + }) + b.Run("string-pointer-noescape", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var s string + C.handleGoStringPointerNoescape(unsafe.Pointer(&s)) + } + }) b.Run("eight-pointers", func(b *testing.B) { var a0, a1, a2, a3, a4, a5, a6, a7 C.VkDeviceCreateInfo for i := 0; i < b.N; i++ { diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go index 7fe6782b9e..7e9a274d05 100644 --- a/src/cmd/cgo/internal/testcshared/cshared_test.go +++ b/src/cmd/cgo/internal/testcshared/cshared_test.go @@ -182,6 +182,8 @@ func run(t *testing.T, extraEnv []string, args ...string) string { if len(extraEnv) > 0 { cmd.Env = append(os.Environ(), extraEnv...) } + stderr := new(strings.Builder) + cmd.Stderr = stderr if GOOS != "windows" { // TestUnexportedSymbols relies on file descriptor 30 @@ -192,11 +194,13 @@ func run(t *testing.T, extraEnv []string, args ...string) string { cmd.ExtraFiles = make([]*os.File, 28) } - out, err := cmd.CombinedOutput() + t.Logf("run: %v", args) + out, err := cmd.Output() + if stderr.Len() > 0 { + t.Logf("stderr:\n%s", stderr) + } if err != nil { t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out) - } else { - t.Logf("run: %v", args) } return string(out) } @@ -602,9 +606,13 @@ func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) { defer os.Remove(bin) defer os.Remove(pkgname + ".h") - out := runExe(t, nil, bin, "./"+libname) + args := []string{bin, "./" + libname} + if testing.Verbose() { + args = append(args, "verbose") + } + out := runExe(t, nil, args...) if strings.TrimSpace(out) != "PASS" { - t.Error(run(t, nil, bin, libname, "verbose")) + t.Errorf("%v%s", args, out) } } diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go b/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go index 4ca44e5894..c70dd681fa 100644 --- a/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go +++ b/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go @@ -31,15 +31,24 @@ func ResetSIGIO() { signal.Reset(syscall.SIGIO) } -// SawSIGIO returns whether we saw a SIGIO within a brief pause. +// AwaitSIGIO blocks indefinitely until a SIGIO is reported. +// +//export AwaitSIGIO +func AwaitSIGIO() { + <-sigioChan +} + +// SawSIGIO reports whether we saw a SIGIO within a brief pause. // //export SawSIGIO -func SawSIGIO() C.int { +func SawSIGIO() bool { + timer := time.NewTimer(100 * time.Millisecond) select { case <-sigioChan: - return 1 - case <-time.After(100 * time.Millisecond): - return 0 + timer.Stop() + return true + case <-timer.C: + return false } } diff --git a/src/cmd/cgo/internal/testcshared/testdata/main4.c b/src/cmd/cgo/internal/testcshared/testdata/main4.c index 6c16364070..467a611ae7 100644 --- a/src/cmd/cgo/internal/testcshared/testdata/main4.c +++ b/src/cmd/cgo/internal/testcshared/testdata/main4.c @@ -88,7 +88,7 @@ int main(int argc, char** argv) { setsid(); if (verbose) { - printf("calling sigaction\n"); + fprintf(stderr, "calling sigaction\n"); } memset(&sa, 0, sizeof sa); @@ -107,7 +107,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling dlopen\n"); + fprintf(stderr, "calling dlopen\n"); } handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL); @@ -117,7 +117,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling dlsym\n"); + fprintf(stderr, "calling dlsym\n"); } // Start some goroutines. @@ -128,7 +128,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling RunGoroutines\n"); + fprintf(stderr, "calling RunGoroutines\n"); } fn(); @@ -137,7 +137,7 @@ int main(int argc, char** argv) { // will be delivered to a goroutine. if (verbose) { - printf("calling pthread_sigmask\n"); + fprintf(stderr, "calling pthread_sigmask\n"); } if (sigemptyset(&mask) < 0) { @@ -153,7 +153,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling kill\n"); + fprintf(stderr, "calling kill\n"); } if (kill(0, SIGIO) < 0) { @@ -161,7 +161,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("waiting for sigioSeen\n"); + fprintf(stderr, "waiting for sigioSeen\n"); } // Wait until the signal has been delivered. @@ -178,13 +178,13 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling setjmp\n"); + fprintf(stderr, "calling setjmp\n"); } // Test that a SIGSEGV on this thread is delivered to us. if (setjmp(jmp) == 0) { if (verbose) { - printf("triggering SIGSEGV\n"); + fprintf(stderr, "triggering SIGSEGV\n"); } *nullPointer = '\0'; @@ -194,7 +194,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling dlsym\n"); + fprintf(stderr, "calling dlsym\n"); } // Make sure that a SIGSEGV in Go causes a run-time panic. @@ -205,7 +205,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling TestSEGV\n"); + fprintf(stderr, "calling TestSEGV\n"); } fn(); diff --git a/src/cmd/cgo/internal/testcshared/testdata/main5.c b/src/cmd/cgo/internal/testcshared/testdata/main5.c index e7bebab1ad..563329e331 100644 --- a/src/cmd/cgo/internal/testcshared/testdata/main5.c +++ b/src/cmd/cgo/internal/testcshared/testdata/main5.c @@ -7,6 +7,7 @@ // This is a lot like ../testcarchive/main3.c. #include <signal.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -29,8 +30,10 @@ int main(int argc, char** argv) { int verbose; struct sigaction sa; void* handle; - void (*fn1)(void); - int (*sawSIGIO)(void); + void (*catchSIGIO)(void); + void (*resetSIGIO)(void); + void (*awaitSIGIO)(); + bool (*sawSIGIO)(); int i; struct timespec ts; @@ -38,7 +41,7 @@ int main(int argc, char** argv) { setvbuf(stdout, NULL, _IONBF, 0); if (verbose) { - printf("calling sigaction\n"); + fprintf(stderr, "calling sigaction\n"); } memset(&sa, 0, sizeof sa); @@ -52,7 +55,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling dlopen\n"); + fprintf(stderr, "calling dlopen\n"); } handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL); @@ -65,7 +68,7 @@ int main(int argc, char** argv) { // installed for SIGIO. if (verbose) { - printf("raising SIGIO\n"); + fprintf(stderr, "raising SIGIO\n"); } if (raise(SIGIO) < 0) { @@ -73,7 +76,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("waiting for sigioSeen\n"); + fprintf(stderr, "waiting for sigioSeen\n"); } // Wait until the signal has been delivered. @@ -94,23 +97,23 @@ int main(int argc, char** argv) { // Tell the Go code to catch SIGIO. if (verbose) { - printf("calling dlsym\n"); + fprintf(stderr, "calling dlsym\n"); } - fn1 = (void(*)(void))dlsym(handle, "CatchSIGIO"); - if (fn1 == NULL) { + catchSIGIO = (void(*)(void))dlsym(handle, "CatchSIGIO"); + if (catchSIGIO == NULL) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } if (verbose) { - printf("calling CatchSIGIO\n"); + fprintf(stderr, "calling CatchSIGIO\n"); } - fn1(); + catchSIGIO(); if (verbose) { - printf("raising SIGIO\n"); + fprintf(stderr, "raising SIGIO\n"); } if (raise(SIGIO) < 0) { @@ -118,24 +121,21 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling dlsym\n"); + fprintf(stderr, "calling dlsym\n"); } // Check that the Go code saw SIGIO. - sawSIGIO = (int (*)(void))dlsym(handle, "SawSIGIO"); - if (sawSIGIO == NULL) { + awaitSIGIO = (void (*)(void))dlsym(handle, "AwaitSIGIO"); + if (awaitSIGIO == NULL) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } if (verbose) { - printf("calling SawSIGIO\n"); + fprintf(stderr, "calling AwaitSIGIO\n"); } - if (!sawSIGIO()) { - fprintf(stderr, "Go handler did not see SIGIO\n"); - exit(EXIT_FAILURE); - } + awaitSIGIO(); if (sigioSeen != 0) { fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n"); @@ -145,23 +145,29 @@ int main(int argc, char** argv) { // Tell the Go code to stop catching SIGIO. if (verbose) { - printf("calling dlsym\n"); + fprintf(stderr, "calling dlsym\n"); } - fn1 = (void(*)(void))dlsym(handle, "ResetSIGIO"); - if (fn1 == NULL) { + resetSIGIO = (void (*)(void))dlsym(handle, "ResetSIGIO"); + if (resetSIGIO == NULL) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } if (verbose) { - printf("calling ResetSIGIO\n"); + fprintf(stderr, "calling ResetSIGIO\n"); } - fn1(); + resetSIGIO(); + + sawSIGIO = (bool (*)(void))dlsym(handle, "SawSIGIO"); + if (sawSIGIO == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } if (verbose) { - printf("raising SIGIO\n"); + fprintf(stderr, "raising SIGIO\n"); } if (raise(SIGIO) < 0) { @@ -169,7 +175,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("calling SawSIGIO\n"); + fprintf(stderr, "calling SawSIGIO\n"); } if (sawSIGIO()) { @@ -178,7 +184,7 @@ int main(int argc, char** argv) { } if (verbose) { - printf("waiting for sigioSeen\n"); + fprintf(stderr, "waiting for sigioSeen\n"); } // Wait until the signal has been delivered. diff --git a/src/cmd/cgo/internal/testerrors/errors_test.go b/src/cmd/cgo/internal/testerrors/errors_test.go index 870d05b491..86236249ca 100644 --- a/src/cmd/cgo/internal/testerrors/errors_test.go +++ b/src/cmd/cgo/internal/testerrors/errors_test.go @@ -39,16 +39,23 @@ func check(t *testing.T, file string) { continue } - _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")) - if !ok { - continue + if _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")); ok { + re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag)) + if err != nil { + t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag) + continue + } + errors = append(errors, re) } - re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag)) - if err != nil { - t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag) - continue + + if _, frag, ok := bytes.Cut(line, []byte("ERROR MESSAGE: ")); ok { + re, err := regexp.Compile(string(frag)) + if err != nil { + t.Errorf("Invalid regexp after `ERROR MESSAGE: `: %#q", frag) + continue + } + errors = append(errors, re) } - errors = append(errors, re) } if len(errors) == 0 { t.Fatalf("cannot find ERROR HERE") @@ -166,3 +173,8 @@ func TestMallocCrashesOnNil(t *testing.T) { t.Fatalf("succeeded unexpectedly") } } + +func TestNotMatchedCFunction(t *testing.T) { + file := "notmatchedcfunction.go" + check(t, file) +} diff --git a/src/cmd/cgo/internal/testerrors/ptr_test.go b/src/cmd/cgo/internal/testerrors/ptr_test.go index 7f56501c58..8fff7615d3 100644 --- a/src/cmd/cgo/internal/testerrors/ptr_test.go +++ b/src/cmd/cgo/internal/testerrors/ptr_test.go @@ -14,7 +14,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "slices" "strings" "sync/atomic" @@ -253,7 +252,10 @@ var ptrTests = []ptrTest{ { // Exported functions may not return Go pointers. name: "export1", - c: `extern unsigned char *GoFn21();`, + c: `#ifdef _WIN32 + __declspec(dllexport) + #endif + extern unsigned char *GoFn21();`, support: `//export GoFn21 func GoFn21() *byte { return new(byte) }`, body: `C.GoFn21()`, @@ -263,6 +265,9 @@ var ptrTests = []ptrTest{ // Returning a C pointer is fine. name: "exportok", c: `#include <stdlib.h> + #ifdef _WIN32 + __declspec(dllexport) + #endif extern unsigned char *GoFn22();`, support: `//export GoFn22 func GoFn22() *byte { return (*byte)(C.malloc(1)) }`, @@ -472,10 +477,6 @@ var ptrTests = []ptrTest{ func TestPointerChecks(t *testing.T) { testenv.MustHaveGoBuild(t) testenv.MustHaveCGO(t) - if runtime.GOOS == "windows" { - // TODO: Skip just the cases that fail? - t.Skipf("some tests fail to build on %s", runtime.GOOS) - } var gopath string var dir string diff --git a/src/cmd/cgo/internal/testerrors/testdata/err5.go b/src/cmd/cgo/internal/testerrors/testdata/err5.go index 779d745ba7..c12a290d38 100644 --- a/src/cmd/cgo/internal/testerrors/testdata/err5.go +++ b/src/cmd/cgo/internal/testerrors/testdata/err5.go @@ -5,6 +5,7 @@ package main //line /tmp/_cgo_.go:1 -//go:cgo_dynamic_linker "/elf/interp" // ERROR HERE: only allowed in cgo-generated code +//go:cgo_dynamic_linker "/elf/interp" +// ERROR MESSAGE: only allowed in cgo-generated code func main() {} diff --git a/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go b/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go new file mode 100644 index 0000000000..5ec9ec5d4a --- /dev/null +++ b/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go @@ -0,0 +1,15 @@ +// 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 main + +/* +// TODO(#56378): change back to "#cgo noescape noMatchedCFunction: no matched C function" in Go 1.23 +// ERROR MESSAGE: #cgo noescape disabled until Go 1.23 +#cgo noescape noMatchedCFunction +*/ +import "C" + +func main() { +} diff --git a/src/cmd/cgo/internal/testfortran/fortran_test.go b/src/cmd/cgo/internal/testfortran/fortran_test.go index eaa36ac7f9..0eae7c5f53 100644 --- a/src/cmd/cgo/internal/testfortran/fortran_test.go +++ b/src/cmd/cgo/internal/testfortran/fortran_test.go @@ -5,7 +5,6 @@ package fortran import ( - "fmt" "internal/testenv" "os" "os/exec" @@ -75,11 +74,18 @@ func TestFortran(t *testing.T) { // Finally, run the actual test. t.Log("go", "run", "./testdata/testprog") - out, err := exec.Command("go", "run", "./testdata/testprog").CombinedOutput() - if err == nil && string(out) != "ok\n" { - err = fmt.Errorf("expected ok") + var stdout, stderr strings.Builder + cmd := exec.Command("go", "run", "./testdata/testprog") + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("%v", cmd) + if stderr.Len() != 0 { + t.Logf("stderr:\n%s", stderr.String()) } if err != nil { - t.Errorf("%s\nOutput:\n%s", err, string(out)) + t.Errorf("%v\n%s", err, stdout.String()) + } else if stdout.String() != "ok\n" { + t.Errorf("stdout:\n%s\nwant \"ok\"", stdout.String()) } } diff --git a/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go b/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go index d8004ceb6d..e98d76c3e6 100644 --- a/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go +++ b/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go @@ -6,7 +6,10 @@ package main // int the_answer(); import "C" -import "os" +import ( + "fmt" + "os" +) func TheAnswer() int { return int(C.the_answer()) @@ -14,8 +17,8 @@ func TheAnswer() int { func main() { if a := TheAnswer(); a != 42 { - println("Unexpected result for The Answer. Got:", a, " Want: 42") + fmt.Fprintln(os.Stderr, "Unexpected result for The Answer. Got:", a, " Want: 42") os.Exit(1) } - println("ok") + fmt.Fprintln(os.Stdout, "ok") } diff --git a/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go b/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go index b0c507477f..d3ab1902c1 100644 --- a/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go +++ b/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Go Authors. All rights reserve d. +// Copyright 2018 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. diff --git a/src/cmd/cgo/internal/testplugin/plugin_test.go b/src/cmd/cgo/internal/testplugin/plugin_test.go index 7f5b1bf4f5..1e32ff8a06 100644 --- a/src/cmd/cgo/internal/testplugin/plugin_test.go +++ b/src/cmd/cgo/internal/testplugin/plugin_test.go @@ -367,25 +367,16 @@ func TestForkExec(t *testing.T) { t.Parallel() goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go") - var cmd *exec.Cmd - done := make(chan int, 1) - - go func() { - for i := 0; i < 100; i++ { - cmd = exec.Command("./forkexec.exe", "1") - err := cmd.Run() - if err != nil { - t.Errorf("running command failed: %v", err) - break + for i := 0; i < 100; i++ { + cmd := testenv.Command(t, "./forkexec.exe", "1") + err := cmd.Run() + if err != nil { + if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { + t.Logf("stderr:\n%s", ee.Stderr) } + t.Errorf("running command failed: %v", err) + break } - done <- 1 - }() - select { - case <-done: - case <-time.After(5 * time.Minute): - cmd.Process.Kill() - t.Fatalf("subprocess hang") } } diff --git a/src/cmd/cgo/internal/testsanitizers/cc_test.go b/src/cmd/cgo/internal/testsanitizers/cc_test.go index 6eb5a64f9b..e650de835a 100644 --- a/src/cmd/cgo/internal/testsanitizers/cc_test.go +++ b/src/cmd/cgo/internal/testsanitizers/cc_test.go @@ -16,8 +16,10 @@ import ( "encoding/json" "errors" "fmt" + "internal/testenv" "os" "os/exec" + "os/user" "path/filepath" "regexp" "strconv" @@ -266,12 +268,28 @@ func compilerSupportsLocation() bool { case "gcc": return compiler.major >= 10 case "clang": + // TODO(65606): The clang toolchain on the LUCI builders is not built against + // zlib, the ASAN runtime can't actually symbolize its own stack trace. Once + // this is resolved, one way or another, switch this back to 'true'. We still + // have coverage from the 'gcc' case above. + if inLUCIBuild() { + return false + } return true default: return false } } +// inLUCIBuild returns true if we're currently executing in a LUCI build. +func inLUCIBuild() bool { + u, err := user.Current() + if err != nil { + return false + } + return testenv.Builder() != "" && u.Username == "swarming" +} + // compilerRequiredTsanVersion reports whether the compiler is the version required by Tsan. // Only restrictions for ppc64le are known; otherwise return true. func compilerRequiredTsanVersion(goos, goarch string) bool { @@ -293,11 +311,17 @@ func compilerRequiredAsanVersion(goos, goarch string) bool { } switch compiler.name { case "gcc": + if goarch == "loong64" { + return compiler.major >= 14 + } if goarch == "ppc64le" { return compiler.major >= 9 } return compiler.major >= 7 case "clang": + if goarch == "loong64" { + return compiler.major >= 16 + } return compiler.major >= 9 default: return false diff --git a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go index f84c9f37ae..3f5b1d91c7 100644 --- a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go +++ b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go @@ -7,11 +7,14 @@ package sanitizers_test import ( + "internal/testenv" "strings" "testing" ) func TestLibFuzzer(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) goos, err := goEnv("GOOS") if err != nil { t.Fatal(err) diff --git a/src/cmd/cgo/internal/testsanitizers/msan_test.go b/src/cmd/cgo/internal/testsanitizers/msan_test.go index 1a22b5246c..83d66f6660 100644 --- a/src/cmd/cgo/internal/testsanitizers/msan_test.go +++ b/src/cmd/cgo/internal/testsanitizers/msan_test.go @@ -8,11 +8,14 @@ package sanitizers_test import ( "internal/platform" + "internal/testenv" "strings" "testing" ) func TestMSAN(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) goos, err := goEnv("GOOS") if err != nil { t.Fatal(err) diff --git a/src/cmd/cgo/internal/testshared/shared_test.go b/src/cmd/cgo/internal/testshared/shared_test.go index 796c46b9bf..814b9994f8 100644 --- a/src/cmd/cgo/internal/testshared/shared_test.go +++ b/src/cmd/cgo/internal/testshared/shared_test.go @@ -96,6 +96,10 @@ func goCmd(t *testing.T, args ...string) string { // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit). func testMain(m *testing.M) (int, error) { + if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { + globalSkip = func(t testing.TB) { t.Skip("short mode and $GO_BUILDER_NAME not set") } + return m.Run(), nil + } if !platform.BuildModeSupported(runtime.Compiler, "shared", runtime.GOOS, runtime.GOARCH) { globalSkip = func(t testing.TB) { t.Skip("shared build mode not supported") } return m.Run(), nil @@ -1155,6 +1159,12 @@ func TestIssue47873(t *testing.T) { goCmd(t, "run", "-linkshared", "./issue47837/main") } +func TestIssue62277(t *testing.T) { + globalSkip(t) + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue62277/p") + goCmd(t, "test", "-linkshared", "./issue62277") +} + // Test that we can build std in shared mode. func TestStd(t *testing.T) { if testing.Short() { diff --git a/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go b/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go index 94f38cf507..18d774b5fc 100644 --- a/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go +++ b/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go @@ -2,6 +2,12 @@ package dep2 import "testshared/depBase" +func init() { + if !depBase.Initialized { + panic("depBase not initialized") + } +} + var W int = 1 var hasProg depBase.HasProg diff --git a/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go b/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go index e7cc7c81eb..a143fe2ff1 100644 --- a/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go +++ b/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go @@ -7,8 +7,24 @@ package depBase import ( "os" "reflect" + + "testshared/depBaseInternal" ) +// Issue 61973: indirect dependencies are not initialized. +func init() { + if !depBaseInternal.Initialized { + panic("depBaseInternal not initialized") + } + if os.Stdout == nil { + panic("os.Stdout is nil") + } + + Initialized = true +} + +var Initialized bool + var SlicePtr interface{} = &[]int{} var V int = 1 diff --git a/src/cmd/cgo/internal/testshared/testdata/depBaseInternal/dep.go b/src/cmd/cgo/internal/testshared/testdata/depBaseInternal/dep.go new file mode 100644 index 0000000000..906bff09c4 --- /dev/null +++ b/src/cmd/cgo/internal/testshared/testdata/depBaseInternal/dep.go @@ -0,0 +1,13 @@ +// 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. + +// depBaseInternal is only imported by depBase. + +package depBaseInternal + +var Initialized bool + +func init() { + Initialized = true +} diff --git a/src/cmd/cgo/internal/testshared/testdata/issue62277/issue62277_test.go b/src/cmd/cgo/internal/testshared/testdata/issue62277/issue62277_test.go new file mode 100644 index 0000000000..89a0601c9b --- /dev/null +++ b/src/cmd/cgo/internal/testshared/testdata/issue62277/issue62277_test.go @@ -0,0 +1,16 @@ +// 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 issue62277_test + +import ( + "testing" + + "testshared/issue62277/p" +) + +func TestIssue62277(t *testing.T) { + t.Log(p.S) + t.Log(p.T) +} diff --git a/src/cmd/cgo/internal/testshared/testdata/issue62277/p/p.go b/src/cmd/cgo/internal/testshared/testdata/issue62277/p/p.go new file mode 100644 index 0000000000..97bde0c10f --- /dev/null +++ b/src/cmd/cgo/internal/testshared/testdata/issue62277/p/p.go @@ -0,0 +1,17 @@ +// 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 p + +var S = func() []string { + return []string{"LD_LIBRARY_PATH"} +}() + +var T []string + +func init() { + T = func() []string { + return []string{"LD_LIBRARY_PATH"} + }() +} |