Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5500cbf
debug/elf: prevent offset overflow
callthingsoff Sep 25, 2025
6d51f93
runtime: jump instead of branch in netbsd/arm64 entry point
qmuntal Sep 29, 2025
d42d56b
encoding/xml: make use of reflect.TypeAssert
apocelipes Sep 24, 2025
fe3ba74
cmd/link: skip TestFlagW on platforms without DWARF symbol table
millerresearch Sep 27, 2025
ae8eba0
cmd/link: use correct length for pcln.cutab
ianlancetaylor Sep 29, 2025
047c2ab
cmd/link: don't pass -Wl,-S on Solaris
cherrymui Sep 26, 2025
4e9006a
crypto/tls: quote protocols in ALPN error message
rolandshoemaker Sep 29, 2025
4b77733
internal/syscall/windows: regenerate GetFileSizeEx
qmuntal Sep 29, 2025
eaf2345
cmd/link: use a .def file to mark exported symbols on Windows
qmuntal Sep 22, 2025
690fc2f
internal/poll: remove buf field from operation
qmuntal Sep 16, 2025
db3cb3f
runtime: correct reference to getStackMap in comment
ianlancetaylor Sep 29, 2025
db4fade
crypto/internal/fips140/mlkem: make CAST conditional
FiloSottile Sep 22, 2025
fc88e18
crypto/internal/fips140/entropy: add CPU jitter-based entropy source
FiloSottile Sep 11, 2025
75c87df
internal/poll: pass the I/O mode instead of an overlapped object in e…
qmuntal Sep 16, 2025
db10db6
internal/poll: remove operation fields from FD
qmuntal Sep 16, 2025
742f920
cmd/compile, runtime: always enable Wasm signext and satconv features
cherrymui Sep 29, 2025
6e95748
cmd/link/internal/arm64: support Mach-O ARM64_RELOC_POINTER_TO_GOT in…
qmuntal Sep 12, 2025
7c8166d
cmd/link/internal/arm64: support Mach-O ARM64_RELOC_SUBTRACTOR in int…
qmuntal Sep 12, 2025
a846bb0
errors: add AsType
jub0bs Sep 29, 2025
300d9d2
runtime: initialise debug settings much earlier in startup process
9muir Sep 18, 2025
97da068
cmd/compile: eliminate nil checks on .dict arg
jakebailey Sep 8, 2025
46ae8b9
cmd/cgo: fix unaligned arguments typedmemmove crash on iOS
timcooijmans Aug 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/next/51945.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg errors, func AsType[$0 error](error) ($0, bool) #51945
2 changes: 2 additions & 0 deletions doc/next/6-stdlib/99-minor/errors/51945.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The new [AsType] function is a generic version of [As]. It is type-safe, faster,
and, in most cases, easier to use.
75 changes: 31 additions & 44 deletions src/cmd/cgo/internal/testcshared/cshared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"bufio"
"bytes"
"cmd/cgo/internal/cgotest"
"cmp"
"debug/elf"
"debug/pe"
"encoding/binary"
Expand Down Expand Up @@ -272,7 +273,7 @@ func createHeaders() error {
// which results in the linkers output implib getting overwritten at each step. So instead build the
// import library the traditional way, using a def file.
err = os.WriteFile("libgo.def",
[]byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"),
[]byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n"),
0644)
if err != nil {
return fmt.Errorf("unable to write def file: %v", err)
Expand Down Expand Up @@ -375,9 +376,23 @@ func TestExportedSymbols(t *testing.T) {
}
}

func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFunctions int, wantAll bool) {
func checkNumberOfExportedSymbolsWindows(t *testing.T, exportedSymbols int, wantAll bool) {
t.Parallel()
tmpdir := t.TempDir()

prog := `
package main
import "C"
func main() {}
`

for i := range exportedSymbols {
prog += fmt.Sprintf(`
//export GoFunc%d
func GoFunc%d() {}
`, i, i)
}

srcfile := filepath.Join(tmpdir, "test.go")
objfile := filepath.Join(tmpdir, "test.dll")
if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
Expand Down Expand Up @@ -443,18 +458,19 @@ func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFu
t.Fatalf("binary.Read failed: %v", err)
}

// Only the two exported functions and _cgo_dummy_export should be exported.
exportedSymbols = cmp.Or(exportedSymbols, 1) // _cgo_stub_export is exported if there are no other symbols exported

// NumberOfNames is the number of functions exported with a unique name.
// NumberOfFunctions can be higher than that because it also counts
// functions exported only by ordinal, a unique number asigned by the linker,
// and linkers might add an unknown number of their own ordinal-only functions.
if wantAll {
if e.NumberOfNames <= uint32(exportedFunctions) {
t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedFunctions)
if e.NumberOfNames <= uint32(exportedSymbols) {
t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedSymbols)
}
} else {
if e.NumberOfNames > uint32(exportedFunctions) {
t.Errorf("got %d exported names, want <= %d", e.NumberOfNames, exportedFunctions)
if e.NumberOfNames != uint32(exportedSymbols) {
t.Errorf("got %d exported names, want %d", e.NumberOfNames, exportedSymbols)
}
}
}
Expand All @@ -470,43 +486,14 @@ func TestNumberOfExportedFunctions(t *testing.T) {

t.Parallel()

const prog0 = `
package main

import "C"

func main() {
}
`

const prog2 = `
package main

import "C"

//export GoFunc
func GoFunc() {
println(42)
}

//export GoFunc2
func GoFunc2() {
println(24)
}

func main() {
}
`
// All programs export _cgo_dummy_export, so add 1 to the expected counts.
t.Run("OnlyExported/0", func(t *testing.T) {
checkNumberOfExportedFunctionsWindows(t, prog0, 0+1, false)
})
t.Run("OnlyExported/2", func(t *testing.T) {
checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, false)
})
t.Run("All", func(t *testing.T) {
checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, true)
})
for i := range 3 {
t.Run(fmt.Sprintf("OnlyExported/%d", i), func(t *testing.T) {
checkNumberOfExportedSymbolsWindows(t, i, false)
})
t.Run(fmt.Sprintf("All/%d", i), func(t *testing.T) {
checkNumberOfExportedSymbolsWindows(t, i, true)
})
}
}

// test1: shared library can be dynamically loaded and exported symbols are accessible.
Expand Down
144 changes: 144 additions & 0 deletions src/cmd/cgo/internal/testout/out_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2025 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 out_test

import (
"bufio"
"bytes"
"fmt"
"internal/testenv"
"internal/goarch"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
)

type methodAlign struct {
Method string
Align int
}

var wantAligns = map[string]int{
"ReturnEmpty": 1,
"ReturnOnlyUint8": 1,
"ReturnOnlyUint16": 2,
"ReturnOnlyUint32": 4,
"ReturnOnlyUint64": goarch.PtrSize,
"ReturnOnlyInt": goarch.PtrSize,
"ReturnOnlyPtr": goarch.PtrSize,
"ReturnByteSlice": goarch.PtrSize,
"ReturnString": goarch.PtrSize,
"InputAndReturnUint8": 1,
"MixedTypes": goarch.PtrSize,
}

// TestAligned tests that the generated _cgo_export.c file has the wanted
// align attributes for struct types used as arguments or results of
// //exported functions.
func TestAligned(t *testing.T) {
testenv.MustHaveGoRun(t)
testenv.MustHaveCGO(t)

testdata, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}

objDir := t.TempDir()

cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "cgo",
"-objdir", objDir,
filepath.Join(testdata, "aligned.go"))
cmd.Stderr = new(bytes.Buffer)

err = cmd.Run()
if err != nil {
t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
}

haveAligns, err := parseAlign(filepath.Join(objDir, "_cgo_export.c"))
if err != nil {
t.Fatal(err)
}

// Check that we have all the wanted methods
if len(haveAligns) != len(wantAligns) {
t.Fatalf("have %d methods with aligned, want %d", len(haveAligns), len(wantAligns))
}

for i := range haveAligns {
method := haveAligns[i].Method
haveAlign := haveAligns[i].Align

wantAlign, ok := wantAligns[method]
if !ok {
t.Errorf("method %s: have aligned %d, want missing entry", method, haveAlign)
} else if haveAlign != wantAlign {
t.Errorf("method %s: have aligned %d, want %d", method, haveAlign, wantAlign)
}
}
}

func parseAlign(filename string) ([]methodAlign, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()

var results []methodAlign
scanner := bufio.NewScanner(file)

// Regex to match function declarations like "struct MethodName_return MethodName("
funcRegex := regexp.MustCompile(`^struct\s+(\w+)_return\s+(\w+)\(`)
// Regex to match simple function declarations like "GoSlice MethodName("
simpleFuncRegex := regexp.MustCompile(`^Go\w+\s+(\w+)\(`)
// Regex to match void-returning exported functions like "void ReturnEmpty("
voidFuncRegex := regexp.MustCompile(`^void\s+(\w+)\(`)
// Regex to match align attributes like "__attribute__((aligned(8)))"
alignRegex := regexp.MustCompile(`__attribute__\(\(aligned\((\d+)\)\)\)`)

var currentMethod string

for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())

// Check if this line declares a function with struct return type
if matches := funcRegex.FindStringSubmatch(line); matches != nil {
currentMethod = matches[2] // Extract the method name
} else if matches := simpleFuncRegex.FindStringSubmatch(line); matches != nil {
// Check if this line declares a function with simple return type (like GoSlice)
currentMethod = matches[1] // Extract the method name
} else if matches := voidFuncRegex.FindStringSubmatch(line); matches != nil {
// Check if this line declares a void-returning function
currentMethod = matches[1] // Extract the method name
}

// Check if this line contains align information
if alignMatches := alignRegex.FindStringSubmatch(line); alignMatches != nil && currentMethod != "" {
alignStr := alignMatches[1]
align, err := strconv.Atoi(alignStr)
if err != nil {
// Skip this entry if we can't parse the align as integer
currentMethod = ""
continue
}
results = append(results, methodAlign{
Method: currentMethod,
Align: align,
})
currentMethod = "" // Reset for next method
}
}

if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading file: %w", err)
}

return results, nil
}
63 changes: 63 additions & 0 deletions src/cmd/cgo/internal/testout/testdata/aligned.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2025 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

import "C"

//export ReturnEmpty
func ReturnEmpty() {
return
}

//export ReturnOnlyUint8
func ReturnOnlyUint8() (uint8, uint8, uint8) {
return 1, 2, 3
}

//export ReturnOnlyUint16
func ReturnOnlyUint16() (uint16, uint16, uint16) {
return 1, 2, 3
}

//export ReturnOnlyUint32
func ReturnOnlyUint32() (uint32, uint32, uint32) {
return 1, 2, 3
}

//export ReturnOnlyUint64
func ReturnOnlyUint64() (uint64, uint64, uint64) {
return 1, 2, 3
}

//export ReturnOnlyInt
func ReturnOnlyInt() (int, int, int) {
return 1, 2, 3
}

//export ReturnOnlyPtr
func ReturnOnlyPtr() (*int, *int, *int) {
a, b, c := 1, 2, 3
return &a, &b, &c
}

//export ReturnString
func ReturnString() string {
return "hello"
}

//export ReturnByteSlice
func ReturnByteSlice() []byte {
return []byte{1, 2, 3}
}

//export InputAndReturnUint8
func InputAndReturnUint8(a, b, c uint8) (uint8, uint8, uint8) {
return a, b, c
}

//export MixedTypes
func MixedTypes(a uint8, b uint16, c uint32, d uint64, e int, f *int) (uint8, uint16, uint32, uint64, int, *int) {
return a, b, c, d, e, f
}
19 changes: 13 additions & 6 deletions src/cmd/cgo/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(gotype, "struct {\n")
off := int64(0)
npad := 0
// the align is at least 1 (for char)
maxAlign := int64(1)
argField := func(typ ast.Expr, namePat string, args ...interface{}) {
name := fmt.Sprintf(namePat, args...)
t := p.cgoType(typ)
Expand All @@ -963,6 +965,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
noSourceConf.Fprint(gotype, fset, typ)
fmt.Fprintf(gotype, "\n")
off += t.Size
// keep track of the maximum alignment among all fields
// so that we can align the struct correctly
if t.Align > maxAlign {
maxAlign = t.Align
}
}
if fn.Recv != nil {
argField(fn.Recv.List[0].Type, "recv")
Expand Down Expand Up @@ -1005,12 +1012,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
}

// Build the wrapper function compiled by gcc.
gccExport := ""
if goos == "windows" {
gccExport = "__declspec(dllexport) "
}
var s strings.Builder
fmt.Fprintf(&s, "%s%s %s(", gccExport, gccResult, exp.ExpName)
fmt.Fprintf(&s, "%s %s(", gccResult, exp.ExpName)
if fn.Recv != nil {
s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String())
s.WriteString(" recv")
Expand Down Expand Up @@ -1051,7 +1054,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
// string.h for memset, and is also robust to C++
// types with constructors. Both GCC and LLVM optimize
// this into just zeroing _cgo_a.
fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype.String(), p.packedAttribute())
//
// The struct should be aligned to the maximum alignment
// of any of its fields. This to avoid alignment
// issues.
fmt.Fprintf(fgcc, "\ttypedef %s %v __attribute__((aligned(%d))) _cgo_argtype;\n", ctype.String(), p.packedAttribute(), maxAlign)
fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n")
fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n")
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
Expand Down
9 changes: 3 additions & 6 deletions src/cmd/compile/internal/ssa/_gen/Wasm.rules
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,9 @@
(ZeroExt32to64 x:(I64Load32U _ _)) => x
(ZeroExt16to(64|32) x:(I64Load16U _ _)) => x
(ZeroExt8to(64|32|16) x:(I64Load8U _ _)) => x
(SignExt32to64 x) && buildcfg.GOWASM.SignExt => (I64Extend32S x)
(SignExt8to(64|32|16) x) && buildcfg.GOWASM.SignExt => (I64Extend8S x)
(SignExt16to(64|32) x) && buildcfg.GOWASM.SignExt => (I64Extend16S x)
(SignExt32to64 x) => (I64ShrS (I64Shl x (I64Const [32])) (I64Const [32]))
(SignExt16to(64|32) x) => (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
(SignExt8to(64|32|16) x) => (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
(SignExt32to64 x) => (I64Extend32S x)
(SignExt8to(64|32|16) x) => (I64Extend8S x)
(SignExt16to(64|32) x) => (I64Extend16S x)
(ZeroExt32to64 x) => (I64And x (I64Const [0xffffffff]))
(ZeroExt16to(64|32) x) => (I64And x (I64Const [0xffff]))
(ZeroExt8to(64|32|16) x) => (I64And x (I64Const [0xff]))
Expand Down
Loading