mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.simd] all: merge master (adce7f1) into dev.simd
Conflicts: - src/internal/goexperiment/flags.go - src/runtime/export_test.go Merge List: + 2025-10-03adce7f196ecmd/link: support .def file with MSVC clang toolchain + 2025-10-03d5b950399dcmd/cgo: fix unaligned arguments typedmemmove crash on iOS + 2025-10-0253845004d6net/http/httputil: deprecate ReverseProxy.Director + 2025-10-02bbdff9e8e1net/http: update bundled x/net/http2 and delete obsolete http2inTests + 2025-10-024008e07080io/fs: move path name documentation up to the package doc comment + 2025-10-020e4e2e6832runtime: skip TestGoroutineLeakProfile under mayMoreStackPreempt + 2025-10-02f03c392295runtime: fix aix/ppc64 library initialization + 2025-10-02707454b41fcmd/go: update `go help mod edit` with the tool and ignore sections + 2025-10-028c68a1c1abruntime,net/http/pprof: goroutine leak detection by using the garbage collector + 2025-10-0284db201ae1cmd/compile: propagate len([]T{}) to make builtin to allow stack allocation + 2025-10-025799c139a7crypto/tls: rm marshalEncryptedClientHelloConfigList dead code + 2025-10-01633dd1d475encoding/json: fix Decoder.InputOffset regression in goexperiment.jsonv2 + 2025-10-018ad27fb656doc/go_spec.html: update date + 2025-10-013f451f2c54testing/synctest: fix inverted test failure message in TestContextAfterFunc + 2025-10-01be0fed8a5fcmd/go/testdata/script/test_fuzz_fuzztime.txt: disable + 2025-09-30eb1c7f6e69runtime: move loong64 library entry point to os-agnostic file + 2025-09-30c9257151e5runtime: unify ppc64/ppc64le library entry point + 2025-09-304ff8a457dbtest/codegen: codify handling of floating point constants on arm64 + 2025-09-30fcb893fc4bcmd/compile/internal/ssa: remove redundant "type:" prefix check + 2025-09-3019cc1022bamime: reduce allocs incurred by ParseMediaType + 2025-09-3008afc50beamime: extend "builtinTypes" to include a more complete list of common types + 2025-09-3097da068774cmd/compile: eliminate nil checks on .dict arg + 2025-09-30300d9d2714runtime: initialise debug settings much earlier in startup process + 2025-09-30a846bb0aa5errors: add AsType + 2025-09-307c8166d02dcmd/link/internal/arm64: support Mach-O ARM64_RELOC_SUBTRACTOR in internal linking + 2025-09-306e95748335cmd/link/internal/arm64: support Mach-O ARM64_RELOC_POINTER_TO_GOT in internal linking + 2025-09-30742f92063ecmd/compile, runtime: always enable Wasm signext and satconv features + 2025-09-30db10db6be3internal/poll: remove operation fields from FD + 2025-09-2975c87df58einternal/poll: pass the I/O mode instead of an overlapped object in execIO + 2025-09-29fc88e18b4acrypto/internal/fips140/entropy: add CPU jitter-based entropy source + 2025-09-29db4fade759crypto/internal/fips140/mlkem: make CAST conditional + 2025-09-29db3cb3fd9aruntime: correct reference to getStackMap in comment + 2025-09-29690fc2fb05internal/poll: remove buf field from operation + 2025-09-29eaf2345256cmd/link: use a .def file to mark exported symbols on Windows + 2025-09-294b77733565internal/syscall/windows: regenerate GetFileSizeEx + 2025-09-294e9006a716crypto/tls: quote protocols in ALPN error message + 2025-09-29047c2ab841cmd/link: don't pass -Wl,-S on Solaris + 2025-09-29ae8eba071bcmd/link: use correct length for pcln.cutab + 2025-09-29fe3ba74b9ecmd/link: skip TestFlagW on platforms without DWARF symbol table + 2025-09-29d42d56b764encoding/xml: make use of reflect.TypeAssert + 2025-09-296d51f93257runtime: jump instead of branch in netbsd/arm64 entry point + 2025-09-285500cbf0e4debug/elf: prevent offset overflow + 2025-09-2734e67623a8all: fix typos + 2025-09-27af6999e60dcmd/compile: implement jump table on loong64 + 2025-09-2663cd912083os/user: simplify go:build + 2025-09-2653009b26ddruntime: use a smaller arena size on Wasm + 2025-09-263a5df9d2b2net/http: add HTTP2Config.StrictMaxConcurrentRequests + 2025-09-2616be34df02net/http: add more tests of transport connection pool + 2025-09-263e4540b49dos/user: use getgrouplist on illumos && cgo + 2025-09-2615fbe3480binternal/poll: simplify WriteMsg and ReadMsg on Windows + 2025-09-2616ae11a9e1runtime: move TestReadMetricsSched to testprog + 2025-09-26459f3a3adccmd/link: don't pass -Wl,-S on AIX + 2025-09-264631a2d3c6cmd/link: skip TestFlagW on AIX + 2025-09-260f31d742cdcmd/compile: fix ICE with new(<untyped expr>) + 2025-09-267d7cd6e07binternal/poll: don't call SetFilePointerEx in Seek for overlapped handles + 2025-09-2641cba31e66mime/multipart: percent-encode CR and LF in header values to avoid CRLF injection + 2025-09-26dd1d597c3aRevert "cmd/internal/obj/loong64: use the MOVVP instruction to optimize prologue" + 2025-09-2645d6bc76afruntime: unify arm64 entry point code + 2025-09-25fdea7da3e6runtime: use common library entry point on windows amd64/386 + 2025-09-25e8a4f508d1lib/fips140: re-seal v1.0.0 + 2025-09-259b7a328089crypto/internal/fips140: remove key import PCTs, make keygen PCTs fatal + 2025-09-257f9ab7203fcrypto/internal/fips140: update frozen module version to "v1.0.0" + 2025-09-25fb5719cbdacrypto/internal/fips140/ecdsa: make TestingOnlyNewDRBG generic + 2025-09-2556067e31f2std: remove unused declarations Change-Id: Iecb28fd62c69fbed59da557f46d31bae55889e2c
This commit is contained in:
commit
fb1749a3fe
260 changed files with 15251 additions and 2505 deletions
1
api/next/51945.txt
Normal file
1
api/next/51945.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pkg errors, func AsType[$0 error](error) ($0, bool) #51945
|
||||||
1
api/next/67813.txt
Normal file
1
api/next/67813.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pkg net/http, type HTTP2Config struct, StrictMaxConcurrentRequests bool #67813
|
||||||
1
api/next/73161.txt
Normal file
1
api/next/73161.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pkg net/http/httputil, type ReverseProxy struct, Director //deprecated #73161
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<!--{
|
<!--{
|
||||||
"Title": "The Go Programming Language Specification",
|
"Title": "The Go Programming Language Specification",
|
||||||
"Subtitle": "Language version go1.25 (Feb 25, 2025)",
|
"Subtitle": "Language version go1.26 (Oct 1, 2025)",
|
||||||
"Path": "/ref/spec"
|
"Path": "/ref/spec"
|
||||||
}-->
|
}-->
|
||||||
|
|
||||||
|
|
|
||||||
2
doc/next/6-stdlib/99-minor/errors/51945.md
Normal file
2
doc/next/6-stdlib/99-minor/errors/51945.md
Normal file
|
|
@ -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.
|
||||||
4
doc/next/6-stdlib/99-minor/net/http/67813.md
Normal file
4
doc/next/6-stdlib/99-minor/net/http/67813.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
The new
|
||||||
|
[HTTP2Config.StrictMaxConcurrentRequests](/pkg/net/http#HTTP2Config.StrictMaxConcurrentRequests)
|
||||||
|
field controls whether a new connection should be opened
|
||||||
|
if an existing HTTP/2 connection has exceeded its stream limit.
|
||||||
11
doc/next/6-stdlib/99-minor/net/http/httputil/73161.md
Normal file
11
doc/next/6-stdlib/99-minor/net/http/httputil/73161.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
The [ReverseProxy.Director] configuration field is deprecated
|
||||||
|
in favor of [ReverseProxy.Rewrite].
|
||||||
|
|
||||||
|
A malicious client can remove headers added by a `Director` function
|
||||||
|
by designating those headers as hop-by-hop. Since there is no way to address
|
||||||
|
this problem within the scope of the `Director` API, we added a new
|
||||||
|
`Rewrite` hook in Go 1.20. `Rewrite` hooks are provided with both the
|
||||||
|
unmodified inbound request received by the proxy and the outbound request
|
||||||
|
which will be sent by the proxy.
|
||||||
|
|
||||||
|
Since the `Director` hook is fundamentally unsafe, we are now deprecating it.
|
||||||
|
|
@ -9,4 +9,4 @@
|
||||||
#
|
#
|
||||||
# go test cmd/go/internal/fips140 -update
|
# go test cmd/go/internal/fips140 -update
|
||||||
#
|
#
|
||||||
v1.0.0.zip b50508feaeff05d22516b21e1fd210bbf5d6a1e422eaf2cfa23fe379342713b8
|
v1.0.0-c2097c7c.zip daf3614e0406f67ae6323c902db3f953a1effb199142362a039e7526dfb9368b
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
v1.0.0
|
v1.0.0-c2097c7c
|
||||||
|
|
|
||||||
Binary file not shown.
1
lib/fips140/v1.0.0.txt
Normal file
1
lib/fips140/v1.0.0.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
v1.0.0-c2097c7c
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmd/cgo/internal/cgotest"
|
"cmd/cgo/internal/cgotest"
|
||||||
|
"cmp"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/pe"
|
"debug/pe"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
@ -272,7 +273,7 @@ func createHeaders() error {
|
||||||
// which results in the linkers output implib getting overwritten at each step. So instead build the
|
// 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.
|
// import library the traditional way, using a def file.
|
||||||
err = os.WriteFile("libgo.def",
|
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)
|
0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to write def file: %v", err)
|
return fmt.Errorf("unable to write def file: %v", err)
|
||||||
|
|
@ -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()
|
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")
|
srcfile := filepath.Join(tmpdir, "test.go")
|
||||||
objfile := filepath.Join(tmpdir, "test.dll")
|
objfile := filepath.Join(tmpdir, "test.dll")
|
||||||
if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
|
if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
|
||||||
|
|
@ -443,18 +458,19 @@ func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFu
|
||||||
t.Fatalf("binary.Read failed: %v", err)
|
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.
|
// NumberOfNames is the number of functions exported with a unique name.
|
||||||
// NumberOfFunctions can be higher than that because it also counts
|
// NumberOfFunctions can be higher than that because it also counts
|
||||||
// functions exported only by ordinal, a unique number asigned by the linker,
|
// 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.
|
// and linkers might add an unknown number of their own ordinal-only functions.
|
||||||
if wantAll {
|
if wantAll {
|
||||||
if e.NumberOfNames <= uint32(exportedFunctions) {
|
if e.NumberOfNames <= uint32(exportedSymbols) {
|
||||||
t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedFunctions)
|
t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedSymbols)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if e.NumberOfNames > uint32(exportedFunctions) {
|
if e.NumberOfNames != uint32(exportedSymbols) {
|
||||||
t.Errorf("got %d exported names, want <= %d", e.NumberOfNames, exportedFunctions)
|
t.Errorf("got %d exported names, want %d", e.NumberOfNames, exportedSymbols)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -470,43 +486,14 @@ func TestNumberOfExportedFunctions(t *testing.T) {
|
||||||
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const prog0 = `
|
for i := range 3 {
|
||||||
package main
|
t.Run(fmt.Sprintf("OnlyExported/%d", i), func(t *testing.T) {
|
||||||
|
checkNumberOfExportedSymbolsWindows(t, i, false)
|
||||||
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) {
|
t.Run(fmt.Sprintf("All/%d", i), func(t *testing.T) {
|
||||||
checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, false)
|
checkNumberOfExportedSymbolsWindows(t, i, true)
|
||||||
})
|
|
||||||
t.Run("All", func(t *testing.T) {
|
|
||||||
checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, true)
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// test1: shared library can be dynamically loaded and exported symbols are accessible.
|
// test1: shared library can be dynamically loaded and exported symbols are accessible.
|
||||||
|
|
|
||||||
144
src/cmd/cgo/internal/testout/out_test.go
Normal file
144
src/cmd/cgo/internal/testout/out_test.go
Normal file
|
|
@ -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
src/cmd/cgo/internal/testout/testdata/aligned.go
vendored
Normal file
63
src/cmd/cgo/internal/testout/testdata/aligned.go
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -949,6 +949,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
||||||
fmt.Fprintf(gotype, "struct {\n")
|
fmt.Fprintf(gotype, "struct {\n")
|
||||||
off := int64(0)
|
off := int64(0)
|
||||||
npad := 0
|
npad := 0
|
||||||
|
// the align is at least 1 (for char)
|
||||||
|
maxAlign := int64(1)
|
||||||
argField := func(typ ast.Expr, namePat string, args ...interface{}) {
|
argField := func(typ ast.Expr, namePat string, args ...interface{}) {
|
||||||
name := fmt.Sprintf(namePat, args...)
|
name := fmt.Sprintf(namePat, args...)
|
||||||
t := p.cgoType(typ)
|
t := p.cgoType(typ)
|
||||||
|
|
@ -963,6 +965,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
||||||
noSourceConf.Fprint(gotype, fset, typ)
|
noSourceConf.Fprint(gotype, fset, typ)
|
||||||
fmt.Fprintf(gotype, "\n")
|
fmt.Fprintf(gotype, "\n")
|
||||||
off += t.Size
|
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 {
|
if fn.Recv != nil {
|
||||||
argField(fn.Recv.List[0].Type, "recv")
|
argField(fn.Recv.List[0].Type, "recv")
|
||||||
|
|
@ -1005,12 +1012,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the wrapper function compiled by gcc.
|
// Build the wrapper function compiled by gcc.
|
||||||
gccExport := ""
|
|
||||||
if goos == "windows" {
|
|
||||||
gccExport = "__declspec(dllexport) "
|
|
||||||
}
|
|
||||||
var s strings.Builder
|
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 {
|
if fn.Recv != nil {
|
||||||
s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String())
|
s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String())
|
||||||
s.WriteString(" recv")
|
s.WriteString(" recv")
|
||||||
|
|
@ -1051,7 +1054,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
||||||
// string.h for memset, and is also robust to C++
|
// string.h for memset, and is also robust to C++
|
||||||
// types with constructors. Both GCC and LLVM optimize
|
// types with constructors. Both GCC and LLVM optimize
|
||||||
// this into just zeroing _cgo_a.
|
// 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, "\tstatic _cgo_argtype _cgo_zero;\n")
|
||||||
fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _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) {
|
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
|
||||||
|
|
|
||||||
|
|
@ -563,7 +563,10 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
||||||
if ro == nil {
|
if ro == nil {
|
||||||
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
|
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
|
||||||
}
|
}
|
||||||
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
|
|
||||||
|
s := ro.StaticValue(*r)
|
||||||
|
switch s.Op() {
|
||||||
|
case ir.OLITERAL:
|
||||||
lit, ok := s.(*ir.BasicLit)
|
lit, ok := s.(*ir.BasicLit)
|
||||||
if !ok || lit.Val().Kind() != constant.Int {
|
if !ok || lit.Val().Kind() != constant.Int {
|
||||||
base.Fatalf("unexpected BasicLit Kind")
|
base.Fatalf("unexpected BasicLit Kind")
|
||||||
|
|
@ -577,6 +580,14 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
||||||
assignTemp(n.Pos(), *r, n.PtrInit())
|
assignTemp(n.Pos(), *r, n.PtrInit())
|
||||||
*r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
|
*r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
|
||||||
}
|
}
|
||||||
|
case ir.OLEN:
|
||||||
|
x := ro.StaticValue(s.(*ir.UnaryExpr).X)
|
||||||
|
if x.Op() == ir.OSLICELIT {
|
||||||
|
x := x.(*ir.CompLitExpr)
|
||||||
|
// Preserve any side effects of the original expression, then update the value.
|
||||||
|
assignTemp(n.Pos(), *r, n.PtrInit())
|
||||||
|
*r = ir.NewBasicLit(n.Pos(), types.Types[types.TINT], constant.MakeInt64(x.Len))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ir.OCONVIFACE:
|
case ir.OCONVIFACE:
|
||||||
|
|
|
||||||
|
|
@ -1266,6 +1266,29 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
|
||||||
p.From.Reg = b.Controls[0].Reg()
|
p.From.Reg = b.Controls[0].Reg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case ssa.BlockLOONG64JUMPTABLE:
|
||||||
|
// ALSLV $3, Rarg0, Rarg1, REGTMP
|
||||||
|
// MOVV (REGTMP), REGTMP
|
||||||
|
// JMP (REGTMP)
|
||||||
|
p := s.Prog(loong64.AALSLV)
|
||||||
|
p.From.Type = obj.TYPE_CONST
|
||||||
|
p.From.Offset = 3 // idx*8
|
||||||
|
p.Reg = b.Controls[0].Reg()
|
||||||
|
p.AddRestSourceReg(b.Controls[1].Reg())
|
||||||
|
p.To.Type = obj.TYPE_REG
|
||||||
|
p.To.Reg = loong64.REGTMP
|
||||||
|
p1 := s.Prog(loong64.AMOVV)
|
||||||
|
p1.From.Type = obj.TYPE_MEM
|
||||||
|
p1.From.Reg = loong64.REGTMP
|
||||||
|
p1.From.Offset = 0
|
||||||
|
p1.To.Type = obj.TYPE_REG
|
||||||
|
p1.To.Reg = loong64.REGTMP
|
||||||
|
p2 := s.Prog(obj.AJMP)
|
||||||
|
p2.To.Type = obj.TYPE_MEM
|
||||||
|
p2.To.Reg = loong64.REGTMP
|
||||||
|
// Save jump tables for later resolution of the target blocks.
|
||||||
|
s.JumpTables = append(s.JumpTables, b)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
b.Fatalf("branch not implemented: %s", b.LongString())
|
b.Fatalf("branch not implemented: %s", b.LongString())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2434,6 +2434,7 @@ func (r *reader) expr() (res ir.Node) {
|
||||||
if r.Bool() {
|
if r.Bool() {
|
||||||
// new(expr) -> tmp := expr; &tmp
|
// new(expr) -> tmp := expr; &tmp
|
||||||
x := r.expr()
|
x := r.expr()
|
||||||
|
x = typecheck.DefaultLit(x, nil) // See TODO in exprConvert case.
|
||||||
var init ir.Nodes
|
var init ir.Nodes
|
||||||
addr := ir.NewAddrExpr(pos, r.tempCopy(pos, x, &init))
|
addr := ir.NewAddrExpr(pos, r.tempCopy(pos, x, &init))
|
||||||
addr.SetInit(init)
|
addr.SetInit(init)
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,8 @@
|
||||||
(MOVBUreg x:((SGT|SGTU) _ _)) => x
|
(MOVBUreg x:((SGT|SGTU) _ _)) => x
|
||||||
(MOVBUreg x:(XOR (MOVVconst [1]) ((SGT|SGTU) _ _))) => x
|
(MOVBUreg x:(XOR (MOVVconst [1]) ((SGT|SGTU) _ _))) => x
|
||||||
|
|
||||||
|
(JumpTable idx) => (JUMPTABLE {makeJumpTableSym(b)} idx (MOVVaddr <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
|
||||||
|
|
||||||
// Write barrier.
|
// Write barrier.
|
||||||
(WB ...) => (LoweredWB ...)
|
(WB ...) => (LoweredWB ...)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -577,6 +577,12 @@ func init() {
|
||||||
{name: "BLT", controls: 2}, // controls[0] < controls[1]
|
{name: "BLT", controls: 2}, // controls[0] < controls[1]
|
||||||
{name: "BGEU", controls: 2}, // controls[0] >= controls[1], unsigned
|
{name: "BGEU", controls: 2}, // controls[0] >= controls[1], unsigned
|
||||||
{name: "BLTU", controls: 2}, // controls[0] < controls[1], unsigned
|
{name: "BLTU", controls: 2}, // controls[0] < controls[1], unsigned
|
||||||
|
|
||||||
|
// JUMPTABLE implements jump tables.
|
||||||
|
// Aux is the symbol (an *obj.LSym) for the jump table.
|
||||||
|
// control[0] is the index into the jump table.
|
||||||
|
// control[1] is the address of the jump table (the address of the symbol stored in Aux).
|
||||||
|
{name: "JUMPTABLE", controls: 2, aux: "Sym"},
|
||||||
}
|
}
|
||||||
|
|
||||||
archs = append(archs, arch{
|
archs = append(archs, arch{
|
||||||
|
|
|
||||||
|
|
@ -55,12 +55,9 @@
|
||||||
(ZeroExt32to64 x:(I64Load32U _ _)) => x
|
(ZeroExt32to64 x:(I64Load32U _ _)) => x
|
||||||
(ZeroExt16to(64|32) x:(I64Load16U _ _)) => x
|
(ZeroExt16to(64|32) x:(I64Load16U _ _)) => x
|
||||||
(ZeroExt8to(64|32|16) x:(I64Load8U _ _)) => x
|
(ZeroExt8to(64|32|16) x:(I64Load8U _ _)) => x
|
||||||
(SignExt32to64 x) && buildcfg.GOWASM.SignExt => (I64Extend32S x)
|
(SignExt32to64 x) => (I64Extend32S x)
|
||||||
(SignExt8to(64|32|16) x) && buildcfg.GOWASM.SignExt => (I64Extend8S x)
|
(SignExt8to(64|32|16) x) => (I64Extend8S x)
|
||||||
(SignExt16to(64|32) x) && buildcfg.GOWASM.SignExt => (I64Extend16S x)
|
(SignExt16to(64|32) x) => (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]))
|
|
||||||
(ZeroExt32to64 x) => (I64And x (I64Const [0xffffffff]))
|
(ZeroExt32to64 x) => (I64And x (I64Const [0xffffffff]))
|
||||||
(ZeroExt16to(64|32) x) => (I64And x (I64Const [0xffff]))
|
(ZeroExt16to(64|32) x) => (I64And x (I64Const [0xffff]))
|
||||||
(ZeroExt8to(64|32|16) x) => (I64And x (I64Const [0xff]))
|
(ZeroExt8to(64|32|16) x) => (I64And x (I64Const [0xff]))
|
||||||
|
|
|
||||||
|
|
@ -2083,6 +2083,9 @@
|
||||||
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
|
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
|
||||||
=> ptr
|
=> ptr
|
||||||
|
|
||||||
|
// .dict args are always non-nil.
|
||||||
|
(NilCheck ptr:(Arg {sym}) _) && isDictArgSym(sym) => ptr
|
||||||
|
|
||||||
// Nil checks of nil checks are redundant.
|
// Nil checks of nil checks are redundant.
|
||||||
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
|
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
|
||||||
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
|
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ const (
|
||||||
BlockLOONG64BLT
|
BlockLOONG64BLT
|
||||||
BlockLOONG64BGEU
|
BlockLOONG64BGEU
|
||||||
BlockLOONG64BLTU
|
BlockLOONG64BLTU
|
||||||
|
BlockLOONG64JUMPTABLE
|
||||||
|
|
||||||
BlockMIPSEQ
|
BlockMIPSEQ
|
||||||
BlockMIPSNE
|
BlockMIPSNE
|
||||||
|
|
@ -264,6 +265,7 @@ var blockString = [...]string{
|
||||||
BlockLOONG64BLT: "BLT",
|
BlockLOONG64BLT: "BLT",
|
||||||
BlockLOONG64BGEU: "BGEU",
|
BlockLOONG64BGEU: "BGEU",
|
||||||
BlockLOONG64BLTU: "BLTU",
|
BlockLOONG64BLTU: "BLTU",
|
||||||
|
BlockLOONG64JUMPTABLE: "JUMPTABLE",
|
||||||
|
|
||||||
BlockMIPSEQ: "EQ",
|
BlockMIPSEQ: "EQ",
|
||||||
BlockMIPSNE: "NE",
|
BlockMIPSNE: "NE",
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/base"
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/logopt"
|
"cmd/compile/internal/logopt"
|
||||||
"cmd/compile/internal/reflectdata"
|
"cmd/compile/internal/reflectdata"
|
||||||
"cmd/compile/internal/rttype"
|
"cmd/compile/internal/rttype"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"cmd/internal/obj/s390x"
|
"cmd/internal/obj/s390x"
|
||||||
|
|
@ -2057,12 +2059,12 @@ func isFixedLoad(v *Value, sym Sym, off int64) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(lsym.Name, "type:") {
|
if ti := lsym.TypeInfo(); ti != nil {
|
||||||
// Type symbols do not contain information about their fields, unlike the cases above.
|
// Type symbols do not contain information about their fields, unlike the cases above.
|
||||||
// Hand-implement field accesses.
|
// Hand-implement field accesses.
|
||||||
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
|
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
|
||||||
|
|
||||||
t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
|
t := ti.Type.(*types.Type)
|
||||||
|
|
||||||
for _, f := range rttype.Type.Fields() {
|
for _, f := range rttype.Type.Fields() {
|
||||||
if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
|
if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
|
||||||
|
|
@ -2116,12 +2118,12 @@ func rewriteFixedLoad(v *Value, sym Sym, sb *Value, off int64) *Value {
|
||||||
base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
|
base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(lsym.Name, "type:") {
|
if ti := lsym.TypeInfo(); ti != nil {
|
||||||
// Type symbols do not contain information about their fields, unlike the cases above.
|
// Type symbols do not contain information about their fields, unlike the cases above.
|
||||||
// Hand-implement field accesses.
|
// Hand-implement field accesses.
|
||||||
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
|
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
|
||||||
|
|
||||||
t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
|
t := ti.Type.(*types.Type)
|
||||||
|
|
||||||
ptrSizedOpConst := OpConst64
|
ptrSizedOpConst := OpConst64
|
||||||
if f.Config.PtrSize == 4 {
|
if f.Config.PtrSize == 4 {
|
||||||
|
|
@ -2611,10 +2613,7 @@ func isDirectType1(v *Value) bool {
|
||||||
return isDirectType2(v.Args[0])
|
return isDirectType2(v.Args[0])
|
||||||
case OpAddr:
|
case OpAddr:
|
||||||
lsym := v.Aux.(*obj.LSym)
|
lsym := v.Aux.(*obj.LSym)
|
||||||
if lsym.Extra == nil {
|
if ti := lsym.TypeInfo(); ti != nil {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
|
|
||||||
return types.IsDirectIface(ti.Type.(*types.Type))
|
return types.IsDirectIface(ti.Type.(*types.Type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2647,10 +2646,7 @@ func isDirectIface1(v *Value, depth int) bool {
|
||||||
return isDirectIface2(v.Args[0], depth-1)
|
return isDirectIface2(v.Args[0], depth-1)
|
||||||
case OpAddr:
|
case OpAddr:
|
||||||
lsym := v.Aux.(*obj.LSym)
|
lsym := v.Aux.(*obj.LSym)
|
||||||
if lsym.Extra == nil {
|
if ii := lsym.ItabInfo(); ii != nil {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ii, ok := (*lsym.Extra).(*obj.ItabInfo); ok {
|
|
||||||
return types.IsDirectIface(ii.Type.(*types.Type))
|
return types.IsDirectIface(ii.Type.(*types.Type))
|
||||||
}
|
}
|
||||||
case OpConstNil:
|
case OpConstNil:
|
||||||
|
|
@ -2744,3 +2740,7 @@ func panicBoundsCToAux(p PanicBoundsC) Aux {
|
||||||
func panicBoundsCCToAux(p PanicBoundsCC) Aux {
|
func panicBoundsCCToAux(p PanicBoundsCC) Aux {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDictArgSym(sym Sym) bool {
|
||||||
|
return sym.(*ir.Name).Sym().Name == typecheck.LocalDictName
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12148,6 +12148,19 @@ func rewriteBlockLOONG64(b *Block) bool {
|
||||||
b.resetWithControl(BlockLOONG64NEZ, v0)
|
b.resetWithControl(BlockLOONG64NEZ, v0)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
case BlockJumpTable:
|
||||||
|
// match: (JumpTable idx)
|
||||||
|
// result: (JUMPTABLE {makeJumpTableSym(b)} idx (MOVVaddr <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
|
||||||
|
for {
|
||||||
|
idx := b.Controls[0]
|
||||||
|
v0 := b.NewValue0(b.Pos, OpLOONG64MOVVaddr, typ.Uintptr)
|
||||||
|
v0.Aux = symToAux(makeJumpTableSym(b))
|
||||||
|
v1 := b.NewValue0(b.Pos, OpSB, typ.Uintptr)
|
||||||
|
v0.AddArg(v1)
|
||||||
|
b.resetWithControl2(BlockLOONG64JUMPTABLE, idx, v0)
|
||||||
|
b.Aux = symToAux(makeJumpTableSym(b))
|
||||||
|
return true
|
||||||
|
}
|
||||||
case BlockLOONG64LEZ:
|
case BlockLOONG64LEZ:
|
||||||
// match: (LEZ (MOVVconst [c]) yes no)
|
// match: (LEZ (MOVVconst [c]) yes no)
|
||||||
// cond: c <= 0
|
// cond: c <= 0
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import "internal/buildcfg"
|
|
||||||
import "math"
|
import "math"
|
||||||
import "cmd/compile/internal/types"
|
import "cmd/compile/internal/types"
|
||||||
|
|
||||||
|
|
@ -3202,8 +3201,6 @@ func rewriteValueWasm_OpRsh8x8(v *Value) bool {
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSignExt16to32(v *Value) bool {
|
func rewriteValueWasm_OpSignExt16to32(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
b := v.Block
|
|
||||||
typ := &b.Func.Config.Types
|
|
||||||
// match: (SignExt16to32 x:(I64Load16S _ _))
|
// match: (SignExt16to32 x:(I64Load16S _ _))
|
||||||
// result: x
|
// result: x
|
||||||
for {
|
for {
|
||||||
|
|
@ -3215,34 +3212,16 @@ func rewriteValueWasm_OpSignExt16to32(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt16to32 x)
|
// match: (SignExt16to32 x)
|
||||||
// cond: buildcfg.GOWASM.SignExt
|
|
||||||
// result: (I64Extend16S x)
|
// result: (I64Extend16S x)
|
||||||
for {
|
for {
|
||||||
x := v_0
|
x := v_0
|
||||||
if !(buildcfg.GOWASM.SignExt) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
v.reset(OpWasmI64Extend16S)
|
v.reset(OpWasmI64Extend16S)
|
||||||
v.AddArg(x)
|
v.AddArg(x)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt16to32 x)
|
|
||||||
// result: (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
|
|
||||||
for {
|
|
||||||
x := v_0
|
|
||||||
v.reset(OpWasmI64ShrS)
|
|
||||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
|
||||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
|
||||||
v1.AuxInt = int64ToAuxInt(48)
|
|
||||||
v0.AddArg2(x, v1)
|
|
||||||
v.AddArg2(v0, v1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSignExt16to64(v *Value) bool {
|
func rewriteValueWasm_OpSignExt16to64(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
b := v.Block
|
|
||||||
typ := &b.Func.Config.Types
|
|
||||||
// match: (SignExt16to64 x:(I64Load16S _ _))
|
// match: (SignExt16to64 x:(I64Load16S _ _))
|
||||||
// result: x
|
// result: x
|
||||||
for {
|
for {
|
||||||
|
|
@ -3254,34 +3233,16 @@ func rewriteValueWasm_OpSignExt16to64(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt16to64 x)
|
// match: (SignExt16to64 x)
|
||||||
// cond: buildcfg.GOWASM.SignExt
|
|
||||||
// result: (I64Extend16S x)
|
// result: (I64Extend16S x)
|
||||||
for {
|
for {
|
||||||
x := v_0
|
x := v_0
|
||||||
if !(buildcfg.GOWASM.SignExt) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
v.reset(OpWasmI64Extend16S)
|
v.reset(OpWasmI64Extend16S)
|
||||||
v.AddArg(x)
|
v.AddArg(x)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt16to64 x)
|
|
||||||
// result: (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
|
|
||||||
for {
|
|
||||||
x := v_0
|
|
||||||
v.reset(OpWasmI64ShrS)
|
|
||||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
|
||||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
|
||||||
v1.AuxInt = int64ToAuxInt(48)
|
|
||||||
v0.AddArg2(x, v1)
|
|
||||||
v.AddArg2(v0, v1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSignExt32to64(v *Value) bool {
|
func rewriteValueWasm_OpSignExt32to64(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
b := v.Block
|
|
||||||
typ := &b.Func.Config.Types
|
|
||||||
// match: (SignExt32to64 x:(I64Load32S _ _))
|
// match: (SignExt32to64 x:(I64Load32S _ _))
|
||||||
// result: x
|
// result: x
|
||||||
for {
|
for {
|
||||||
|
|
@ -3293,34 +3254,16 @@ func rewriteValueWasm_OpSignExt32to64(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt32to64 x)
|
// match: (SignExt32to64 x)
|
||||||
// cond: buildcfg.GOWASM.SignExt
|
|
||||||
// result: (I64Extend32S x)
|
// result: (I64Extend32S x)
|
||||||
for {
|
for {
|
||||||
x := v_0
|
x := v_0
|
||||||
if !(buildcfg.GOWASM.SignExt) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
v.reset(OpWasmI64Extend32S)
|
v.reset(OpWasmI64Extend32S)
|
||||||
v.AddArg(x)
|
v.AddArg(x)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt32to64 x)
|
|
||||||
// result: (I64ShrS (I64Shl x (I64Const [32])) (I64Const [32]))
|
|
||||||
for {
|
|
||||||
x := v_0
|
|
||||||
v.reset(OpWasmI64ShrS)
|
|
||||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
|
||||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
|
||||||
v1.AuxInt = int64ToAuxInt(32)
|
|
||||||
v0.AddArg2(x, v1)
|
|
||||||
v.AddArg2(v0, v1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSignExt8to16(v *Value) bool {
|
func rewriteValueWasm_OpSignExt8to16(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
b := v.Block
|
|
||||||
typ := &b.Func.Config.Types
|
|
||||||
// match: (SignExt8to16 x:(I64Load8S _ _))
|
// match: (SignExt8to16 x:(I64Load8S _ _))
|
||||||
// result: x
|
// result: x
|
||||||
for {
|
for {
|
||||||
|
|
@ -3332,34 +3275,16 @@ func rewriteValueWasm_OpSignExt8to16(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt8to16 x)
|
// match: (SignExt8to16 x)
|
||||||
// cond: buildcfg.GOWASM.SignExt
|
|
||||||
// result: (I64Extend8S x)
|
// result: (I64Extend8S x)
|
||||||
for {
|
for {
|
||||||
x := v_0
|
x := v_0
|
||||||
if !(buildcfg.GOWASM.SignExt) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
v.reset(OpWasmI64Extend8S)
|
v.reset(OpWasmI64Extend8S)
|
||||||
v.AddArg(x)
|
v.AddArg(x)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt8to16 x)
|
|
||||||
// result: (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
|
||||||
for {
|
|
||||||
x := v_0
|
|
||||||
v.reset(OpWasmI64ShrS)
|
|
||||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
|
||||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
|
||||||
v1.AuxInt = int64ToAuxInt(56)
|
|
||||||
v0.AddArg2(x, v1)
|
|
||||||
v.AddArg2(v0, v1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSignExt8to32(v *Value) bool {
|
func rewriteValueWasm_OpSignExt8to32(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
b := v.Block
|
|
||||||
typ := &b.Func.Config.Types
|
|
||||||
// match: (SignExt8to32 x:(I64Load8S _ _))
|
// match: (SignExt8to32 x:(I64Load8S _ _))
|
||||||
// result: x
|
// result: x
|
||||||
for {
|
for {
|
||||||
|
|
@ -3371,34 +3296,16 @@ func rewriteValueWasm_OpSignExt8to32(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt8to32 x)
|
// match: (SignExt8to32 x)
|
||||||
// cond: buildcfg.GOWASM.SignExt
|
|
||||||
// result: (I64Extend8S x)
|
// result: (I64Extend8S x)
|
||||||
for {
|
for {
|
||||||
x := v_0
|
x := v_0
|
||||||
if !(buildcfg.GOWASM.SignExt) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
v.reset(OpWasmI64Extend8S)
|
v.reset(OpWasmI64Extend8S)
|
||||||
v.AddArg(x)
|
v.AddArg(x)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt8to32 x)
|
|
||||||
// result: (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
|
||||||
for {
|
|
||||||
x := v_0
|
|
||||||
v.reset(OpWasmI64ShrS)
|
|
||||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
|
||||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
|
||||||
v1.AuxInt = int64ToAuxInt(56)
|
|
||||||
v0.AddArg2(x, v1)
|
|
||||||
v.AddArg2(v0, v1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSignExt8to64(v *Value) bool {
|
func rewriteValueWasm_OpSignExt8to64(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
b := v.Block
|
|
||||||
typ := &b.Func.Config.Types
|
|
||||||
// match: (SignExt8to64 x:(I64Load8S _ _))
|
// match: (SignExt8to64 x:(I64Load8S _ _))
|
||||||
// result: x
|
// result: x
|
||||||
for {
|
for {
|
||||||
|
|
@ -3410,29 +3317,13 @@ func rewriteValueWasm_OpSignExt8to64(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt8to64 x)
|
// match: (SignExt8to64 x)
|
||||||
// cond: buildcfg.GOWASM.SignExt
|
|
||||||
// result: (I64Extend8S x)
|
// result: (I64Extend8S x)
|
||||||
for {
|
for {
|
||||||
x := v_0
|
x := v_0
|
||||||
if !(buildcfg.GOWASM.SignExt) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
v.reset(OpWasmI64Extend8S)
|
v.reset(OpWasmI64Extend8S)
|
||||||
v.AddArg(x)
|
v.AddArg(x)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SignExt8to64 x)
|
|
||||||
// result: (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
|
||||||
for {
|
|
||||||
x := v_0
|
|
||||||
v.reset(OpWasmI64ShrS)
|
|
||||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
|
||||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
|
||||||
v1.AuxInt = int64ToAuxInt(56)
|
|
||||||
v0.AddArg2(x, v1)
|
|
||||||
v.AddArg2(v0, v1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func rewriteValueWasm_OpSlicemask(v *Value) bool {
|
func rewriteValueWasm_OpSlicemask(v *Value) bool {
|
||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
|
|
|
||||||
|
|
@ -21395,6 +21395,21 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
|
||||||
v.copyOf(ptr)
|
v.copyOf(ptr)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// match: (NilCheck ptr:(Arg {sym}) _)
|
||||||
|
// cond: isDictArgSym(sym)
|
||||||
|
// result: ptr
|
||||||
|
for {
|
||||||
|
ptr := v_0
|
||||||
|
if ptr.Op != OpArg {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sym := auxToSym(ptr.Aux)
|
||||||
|
if !(isDictArgSym(sym)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.copyOf(ptr)
|
||||||
|
return true
|
||||||
|
}
|
||||||
// match: (NilCheck ptr:(NilCheck _ _) _ )
|
// match: (NilCheck ptr:(NilCheck _ _) _ )
|
||||||
// result: ptr
|
// result: ptr
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"cmd/internal/obj/wasm"
|
"cmd/internal/obj/wasm"
|
||||||
"internal/buildcfg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -425,27 +424,11 @@ func ssaGenValueOnStack(s *ssagen.State, v *ssa.Value, extend bool) {
|
||||||
|
|
||||||
case ssa.OpWasmI64TruncSatF32S, ssa.OpWasmI64TruncSatF64S:
|
case ssa.OpWasmI64TruncSatF32S, ssa.OpWasmI64TruncSatF64S:
|
||||||
getValue64(s, v.Args[0])
|
getValue64(s, v.Args[0])
|
||||||
if buildcfg.GOWASM.SatConv {
|
|
||||||
s.Prog(v.Op.Asm())
|
s.Prog(v.Op.Asm())
|
||||||
} else {
|
|
||||||
if v.Op == ssa.OpWasmI64TruncSatF32S {
|
|
||||||
s.Prog(wasm.AF64PromoteF32)
|
|
||||||
}
|
|
||||||
p := s.Prog(wasm.ACall)
|
|
||||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncS}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ssa.OpWasmI64TruncSatF32U, ssa.OpWasmI64TruncSatF64U:
|
case ssa.OpWasmI64TruncSatF32U, ssa.OpWasmI64TruncSatF64U:
|
||||||
getValue64(s, v.Args[0])
|
getValue64(s, v.Args[0])
|
||||||
if buildcfg.GOWASM.SatConv {
|
|
||||||
s.Prog(v.Op.Asm())
|
s.Prog(v.Op.Asm())
|
||||||
} else {
|
|
||||||
if v.Op == ssa.OpWasmI64TruncSatF32U {
|
|
||||||
s.Prog(wasm.AF64PromoteF32)
|
|
||||||
}
|
|
||||||
p := s.Prog(wasm.ACall)
|
|
||||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncU}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ssa.OpWasmF32DemoteF64:
|
case ssa.OpWasmF32DemoteF64:
|
||||||
getValue64(s, v.Args[0])
|
getValue64(s, v.Args[0])
|
||||||
|
|
|
||||||
|
|
@ -1280,11 +1280,6 @@
|
||||||
// The -json flag prints the final go.mod file in JSON format instead of
|
// The -json flag prints the final go.mod file in JSON format instead of
|
||||||
// writing it back to go.mod. The JSON output corresponds to these Go types:
|
// writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||||
//
|
//
|
||||||
// type Module struct {
|
|
||||||
// Path string
|
|
||||||
// Version string
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// type GoMod struct {
|
// type GoMod struct {
|
||||||
// Module ModPath
|
// Module ModPath
|
||||||
// Go string
|
// Go string
|
||||||
|
|
@ -1294,6 +1289,13 @@
|
||||||
// Exclude []Module
|
// Exclude []Module
|
||||||
// Replace []Replace
|
// Replace []Replace
|
||||||
// Retract []Retract
|
// Retract []Retract
|
||||||
|
// Tool []Tool
|
||||||
|
// Ignore []Ignore
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type Module struct {
|
||||||
|
// Path string
|
||||||
|
// Version string
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// type ModPath struct {
|
// type ModPath struct {
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/mod/module"
|
"golang.org/x/mod/module"
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
modzip "golang.org/x/mod/zip"
|
modzip "golang.org/x/mod/zip"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ func main() {
|
||||||
|
|
||||||
// Must have valid version, and must not overwrite existing file.
|
// Must have valid version, and must not overwrite existing file.
|
||||||
version := flag.Arg(0)
|
version := flag.Arg(0)
|
||||||
if !regexp.MustCompile(`^v\d+\.\d+\.\d+$`).MatchString(version) {
|
if semver.Canonical(version) != version {
|
||||||
log.Fatalf("invalid version %q; must be vX.Y.Z", version)
|
log.Fatalf("invalid version %q; must be vX.Y.Z", version)
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(version + ".zip"); err == nil {
|
if _, err := os.Stat(version + ".zip"); err == nil {
|
||||||
|
|
@ -117,7 +117,9 @@ func main() {
|
||||||
if !bytes.Contains(contents, []byte(returnLine)) {
|
if !bytes.Contains(contents, []byte(returnLine)) {
|
||||||
log.Fatalf("did not find %q in fips140.go", returnLine)
|
log.Fatalf("did not find %q in fips140.go", returnLine)
|
||||||
}
|
}
|
||||||
newLine := `return "` + version + `"`
|
// Use only the vX.Y.Z part of a possible vX.Y.Z-hash version.
|
||||||
|
v, _, _ := strings.Cut(version, "-")
|
||||||
|
newLine := `return "` + v + `"`
|
||||||
contents = bytes.ReplaceAll(contents, []byte(returnLine), []byte(newLine))
|
contents = bytes.ReplaceAll(contents, []byte(returnLine), []byte(newLine))
|
||||||
wf, err := zw.Create(f.Name)
|
wf, err := zw.Create(f.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -104,11 +104,6 @@ writing it back to go.mod.
|
||||||
The -json flag prints the final go.mod file in JSON format instead of
|
The -json flag prints the final go.mod file in JSON format instead of
|
||||||
writing it back to go.mod. The JSON output corresponds to these Go types:
|
writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||||
|
|
||||||
type Module struct {
|
|
||||||
Path string
|
|
||||||
Version string
|
|
||||||
}
|
|
||||||
|
|
||||||
type GoMod struct {
|
type GoMod struct {
|
||||||
Module ModPath
|
Module ModPath
|
||||||
Go string
|
Go string
|
||||||
|
|
@ -118,6 +113,13 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||||
Exclude []Module
|
Exclude []Module
|
||||||
Replace []Replace
|
Replace []Replace
|
||||||
Retract []Retract
|
Retract []Retract
|
||||||
|
Tool []Tool
|
||||||
|
Ignore []Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
type Module struct {
|
||||||
|
Path string
|
||||||
|
Version string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModPath struct {
|
type ModPath struct {
|
||||||
|
|
|
||||||
5
src/cmd/go/testdata/script/fipssnap.txt
vendored
5
src/cmd/go/testdata/script/fipssnap.txt
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
env snap=v1.0.0
|
env snap=v1.0.0-c2097c7c
|
||||||
env alias=inprocess
|
env alias=inprocess
|
||||||
|
|
||||||
env GOFIPS140=$snap
|
env GOFIPS140=$snap
|
||||||
|
|
@ -23,8 +23,7 @@ stdout crypto/internal/fips140/$snap/sha256
|
||||||
! stdout crypto/internal/fips140/check
|
! stdout crypto/internal/fips140/check
|
||||||
|
|
||||||
# again with GOFIPS140=$alias
|
# again with GOFIPS140=$alias
|
||||||
# TODO: enable when we add inprocess.txt
|
env GOFIPS140=$alias
|
||||||
# env GOFIPS140=$alias
|
|
||||||
|
|
||||||
# default GODEBUG includes fips140=on
|
# default GODEBUG includes fips140=on
|
||||||
go list -f '{{.DefaultGODEBUG}}'
|
go list -f '{{.DefaultGODEBUG}}'
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
skip # a 5s timeout is never going to be reliable (go.dev/issue/72140)
|
||||||
|
|
||||||
[!fuzz] skip
|
[!fuzz] skip
|
||||||
[short] skip
|
[short] skip
|
||||||
env GOCACHE=$WORK/cache
|
env GOCACHE=$WORK/cache
|
||||||
|
|
|
||||||
|
|
@ -464,7 +464,7 @@ type LSym struct {
|
||||||
P []byte
|
P []byte
|
||||||
R []Reloc
|
R []Reloc
|
||||||
|
|
||||||
Extra *interface{} // *FuncInfo, *VarInfo, *FileInfo, or *TypeInfo, if present
|
Extra *interface{} // *FuncInfo, *VarInfo, *FileInfo, *TypeInfo, or *ItabInfo, if present
|
||||||
|
|
||||||
Pkg string
|
Pkg string
|
||||||
PkgIdx int32
|
PkgIdx int32
|
||||||
|
|
@ -604,6 +604,15 @@ func (s *LSym) NewTypeInfo() *TypeInfo {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TypeInfo returns the *TypeInfo associated with s, or else nil.
|
||||||
|
func (s *LSym) TypeInfo() *TypeInfo {
|
||||||
|
if s.Extra == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t, _ := (*s.Extra).(*TypeInfo)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// An ItabInfo contains information for a symbol
|
// An ItabInfo contains information for a symbol
|
||||||
// that contains a runtime.itab.
|
// that contains a runtime.itab.
|
||||||
type ItabInfo struct {
|
type ItabInfo struct {
|
||||||
|
|
@ -620,6 +629,15 @@ func (s *LSym) NewItabInfo() *ItabInfo {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ItabInfo returns the *ItabInfo associated with s, or else nil.
|
||||||
|
func (s *LSym) ItabInfo() *ItabInfo {
|
||||||
|
if s.Extra == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
i, _ := (*s.Extra).(*ItabInfo)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
// WasmImport represents a WebAssembly (WASM) imported function with
|
// WasmImport represents a WebAssembly (WASM) imported function with
|
||||||
// parameters and results translated into WASM types based on the Go function
|
// parameters and results translated into WASM types based on the Go function
|
||||||
// declaration.
|
// declaration.
|
||||||
|
|
|
||||||
|
|
@ -707,6 +707,15 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
// so instruction sequences that use REGTMP are unsafe to
|
// so instruction sequences that use REGTMP are unsafe to
|
||||||
// preempt asynchronously.
|
// preempt asynchronously.
|
||||||
obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable)
|
obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable)
|
||||||
|
|
||||||
|
// Now that we know byte offsets, we can generate jump table entries.
|
||||||
|
for _, jt := range cursym.Func().JumpTables {
|
||||||
|
for i, p := range jt.Targets {
|
||||||
|
// The ith jumptable entry points to the p.Pc'th
|
||||||
|
// byte in the function symbol s.
|
||||||
|
jt.Sym.WriteAddr(ctxt, int64(i)*8, 8, cursym, p.Pc)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isUnsafePoint returns whether p is an unsafe point.
|
// isUnsafePoint returns whether p is an unsafe point.
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
q = c.ctxt.StartUnsafePoint(q, c.newprog)
|
q = c.ctxt.StartUnsafePoint(q, c.newprog)
|
||||||
|
|
||||||
q = obj.Appendp(q, newprog)
|
q = obj.Appendp(q, newprog)
|
||||||
q.As = AMOVVP
|
q.As = mov
|
||||||
q.Pos = p.Pos
|
q.Pos = p.Pos
|
||||||
q.From.Type = obj.TYPE_REG
|
q.From.Type = obj.TYPE_REG
|
||||||
q.From.Reg = REGLINK
|
q.From.Reg = REGLINK
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ var ArchLoong64 = &Arch{
|
||||||
MinLC: 4,
|
MinLC: 4,
|
||||||
Alignment: 8, // Unaligned accesses are not guaranteed to be fast
|
Alignment: 8, // Unaligned accesses are not guaranteed to be fast
|
||||||
CanMergeLoads: true,
|
CanMergeLoads: true,
|
||||||
|
CanJumpTable: true,
|
||||||
HasLR: true,
|
HasLR: true,
|
||||||
FixedFrameSize: 8, // LR
|
FixedFrameSize: 8, // LR
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -361,6 +361,13 @@ func TestDWARFLocationList(t *testing.T) {
|
||||||
|
|
||||||
func TestFlagW(t *testing.T) {
|
func TestFlagW(t *testing.T) {
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
|
if runtime.GOOS == "aix" {
|
||||||
|
t.Skip("internal/xcoff cannot parse file without symbol table")
|
||||||
|
}
|
||||||
|
if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) {
|
||||||
|
t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
tmpdir := t.TempDir()
|
tmpdir := t.TempDir()
|
||||||
|
|
@ -379,7 +386,7 @@ func TestFlagW(t *testing.T) {
|
||||||
{"-s", false}, // -s implies -w
|
{"-s", false}, // -s implies -w
|
||||||
{"-s -w=0", true}, // -w=0 negates the implied -w
|
{"-s -w=0", true}, // -w=0 negates the implied -w
|
||||||
}
|
}
|
||||||
if testenv.HasCGO() {
|
if testenv.HasCGO() && runtime.GOOS != "solaris" { // Solaris linker doesn't support the -S flag
|
||||||
tests = append(tests,
|
tests = append(tests,
|
||||||
testCase{"-w -linkmode=external", false},
|
testCase{"-w -linkmode=external", false},
|
||||||
testCase{"-s -linkmode=external", false},
|
testCase{"-s -linkmode=external", false},
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,28 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_SUBTRACTOR*2:
|
||||||
|
// ARM64_RELOC_SUBTRACTOR must be followed by ARM64_RELOC_UNSIGNED.
|
||||||
|
// The pair of relocations resolves to the difference between two
|
||||||
|
// symbol addresses (each relocation specifies a symbol).
|
||||||
|
outer, off := ld.FoldSubSymbolOffset(ldr, targ)
|
||||||
|
if outer != s {
|
||||||
|
// TODO: support subtracted symbol in different section.
|
||||||
|
ldr.Errorf(s, "unsupported ARM64_RELOC_SUBTRACTOR reloc: target %s, outer %s", ldr.SymName(targ), ldr.SymName(outer))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
su := ldr.MakeSymbolUpdater(s)
|
||||||
|
relocs := su.Relocs()
|
||||||
|
if rIdx+1 >= relocs.Count() || relocs.At(rIdx+1).Type() != objabi.MachoRelocOffset+ld.MACHO_ARM64_RELOC_UNSIGNED*2 || relocs.At(rIdx+1).Off() != r.Off() {
|
||||||
|
ldr.Errorf(s, "unexpected ARM64_RELOC_SUBTRACTOR reloc, must be followed by ARM64_RELOC_UNSIGNED at same offset")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
su.SetRelocType(rIdx+1, objabi.R_PCREL)
|
||||||
|
su.SetRelocAdd(rIdx+1, r.Add()+int64(r.Off())+int64(r.Siz())-off)
|
||||||
|
// Remove the other relocation
|
||||||
|
su.SetRelocSiz(rIdx, 0)
|
||||||
|
return true
|
||||||
|
|
||||||
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
|
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
|
||||||
su := ldr.MakeSymbolUpdater(s)
|
su := ldr.MakeSymbolUpdater(s)
|
||||||
su.SetRelocType(rIdx, objabi.R_CALLARM64)
|
su.SetRelocType(rIdx, objabi.R_CALLARM64)
|
||||||
|
|
@ -277,6 +299,17 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||||
su.SetRelocSym(rIdx, syms.GOT)
|
su.SetRelocSym(rIdx, syms.GOT)
|
||||||
su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ)))
|
su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ)))
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_POINTER_TO_GOT*2 + pcrel:
|
||||||
|
if targType != sym.SDYNIMPORT {
|
||||||
|
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
|
||||||
|
}
|
||||||
|
ld.AddGotSym(target, ldr, syms, targ, 0)
|
||||||
|
su := ldr.MakeSymbolUpdater(s)
|
||||||
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
||||||
|
su.SetRelocSym(rIdx, syms.GOT)
|
||||||
|
su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())+int64(ldr.SymGot(targ)))
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reread the reloc to incorporate any changes in type above.
|
// Reread the reloc to incorporate any changes in type above.
|
||||||
|
|
|
||||||
|
|
@ -1452,8 +1452,10 @@ func (ctxt *Link) hostlink() {
|
||||||
argv = append(argv, "-s")
|
argv = append(argv, "-s")
|
||||||
}
|
}
|
||||||
} else if *FlagW {
|
} else if *FlagW {
|
||||||
|
if !ctxt.IsAIX() && !ctxt.IsSolaris() { // The AIX and Solaris linkers' -S has different meaning
|
||||||
argv = append(argv, "-Wl,-S") // suppress debugging symbols
|
argv = append(argv, "-Wl,-S") // suppress debugging symbols
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// On darwin, whether to combine DWARF into executable.
|
// On darwin, whether to combine DWARF into executable.
|
||||||
// Only macOS supports unmapped segments such as our __DWARF segment.
|
// Only macOS supports unmapped segments such as our __DWARF segment.
|
||||||
|
|
@ -1770,7 +1772,8 @@ func (ctxt *Link) hostlink() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force global symbols to be exported for dlopen, etc.
|
// Force global symbols to be exported for dlopen, etc.
|
||||||
if ctxt.IsELF {
|
switch {
|
||||||
|
case ctxt.IsELF:
|
||||||
if ctxt.DynlinkingGo() || ctxt.BuildMode == BuildModeCShared || !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "-Wl,--export-dynamic-symbol=main") {
|
if ctxt.DynlinkingGo() || ctxt.BuildMode == BuildModeCShared || !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "-Wl,--export-dynamic-symbol=main") {
|
||||||
argv = append(argv, "-rdynamic")
|
argv = append(argv, "-rdynamic")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1781,10 +1784,16 @@ func (ctxt *Link) hostlink() {
|
||||||
sort.Strings(exports)
|
sort.Strings(exports)
|
||||||
argv = append(argv, exports...)
|
argv = append(argv, exports...)
|
||||||
}
|
}
|
||||||
}
|
case ctxt.IsAIX():
|
||||||
if ctxt.HeadType == objabi.Haix {
|
|
||||||
fileName := xcoffCreateExportFile(ctxt)
|
fileName := xcoffCreateExportFile(ctxt)
|
||||||
argv = append(argv, "-Wl,-bE:"+fileName)
|
argv = append(argv, "-Wl,-bE:"+fileName)
|
||||||
|
case ctxt.IsWindows() && !slices.Contains(flagExtldflags, wlPrefix+"export-all-symbols"):
|
||||||
|
fileName := peCreateExportFile(ctxt, filepath.Base(outopt))
|
||||||
|
prefix := ""
|
||||||
|
if isMSVC {
|
||||||
|
prefix = "-Wl,-def:"
|
||||||
|
}
|
||||||
|
argv = append(argv, prefix+fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
const unusedArguments = "-Qunused-arguments"
|
const unusedArguments = "-Qunused-arguments"
|
||||||
|
|
|
||||||
|
|
@ -106,11 +106,13 @@ const (
|
||||||
MACHO_ARM_RELOC_SECTDIFF = 2
|
MACHO_ARM_RELOC_SECTDIFF = 2
|
||||||
MACHO_ARM_RELOC_BR24 = 5
|
MACHO_ARM_RELOC_BR24 = 5
|
||||||
MACHO_ARM64_RELOC_UNSIGNED = 0
|
MACHO_ARM64_RELOC_UNSIGNED = 0
|
||||||
|
MACHO_ARM64_RELOC_SUBTRACTOR = 1
|
||||||
MACHO_ARM64_RELOC_BRANCH26 = 2
|
MACHO_ARM64_RELOC_BRANCH26 = 2
|
||||||
MACHO_ARM64_RELOC_PAGE21 = 3
|
MACHO_ARM64_RELOC_PAGE21 = 3
|
||||||
MACHO_ARM64_RELOC_PAGEOFF12 = 4
|
MACHO_ARM64_RELOC_PAGEOFF12 = 4
|
||||||
MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
|
MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
|
||||||
MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
|
MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
|
||||||
|
MACHO_ARM64_RELOC_POINTER_TO_GOT = 7
|
||||||
MACHO_ARM64_RELOC_ADDEND = 10
|
MACHO_ARM64_RELOC_ADDEND = 10
|
||||||
MACHO_GENERIC_RELOC_VANILLA = 0
|
MACHO_GENERIC_RELOC_VANILLA = 0
|
||||||
MACHO_FAKE_GOTPCREL = 100
|
MACHO_FAKE_GOTPCREL = 100
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
package ld
|
package ld
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"cmd/internal/objabi"
|
"cmd/internal/objabi"
|
||||||
"cmd/internal/sys"
|
"cmd/internal/sys"
|
||||||
"cmd/link/internal/loader"
|
"cmd/link/internal/loader"
|
||||||
|
|
@ -17,6 +18,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"internal/buildcfg"
|
"internal/buildcfg"
|
||||||
"math"
|
"math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -1748,3 +1751,44 @@ func asmbPe(ctxt *Link) {
|
||||||
|
|
||||||
pewrite(ctxt)
|
pewrite(ctxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// peCreateExportFile creates a file with exported symbols for Windows .def files.
|
||||||
|
// ld will export all symbols, even those not marked for export, unless a .def file is provided.
|
||||||
|
func peCreateExportFile(ctxt *Link, libName string) (fname string) {
|
||||||
|
fname = filepath.Join(*flagTmpdir, "export_file.def")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "LIBRARY %s\n", libName)
|
||||||
|
buf.WriteString("EXPORTS\n")
|
||||||
|
|
||||||
|
ldr := ctxt.loader
|
||||||
|
var exports []string
|
||||||
|
for s := range ldr.ForAllCgoExportStatic() {
|
||||||
|
extname := ldr.SymExtname(s)
|
||||||
|
if !strings.HasPrefix(extname, "_cgoexp_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ldr.IsFileLocal(s) {
|
||||||
|
continue // Only export non-static symbols
|
||||||
|
}
|
||||||
|
// Retrieve the name of the initial symbol
|
||||||
|
// exported by cgo.
|
||||||
|
// The corresponding Go symbol is:
|
||||||
|
// _cgoexp_hashcode_symname.
|
||||||
|
name := strings.SplitN(extname, "_", 4)[3]
|
||||||
|
exports = append(exports, name)
|
||||||
|
}
|
||||||
|
if len(exports) == 0 {
|
||||||
|
// See runtime/cgo/windows.go for details.
|
||||||
|
exports = append(exports, "_cgo_stub_export")
|
||||||
|
}
|
||||||
|
sort.Strings(exports)
|
||||||
|
buf.WriteString(strings.Join(exports, "\n"))
|
||||||
|
|
||||||
|
err := os.WriteFile(fname, buf.Bytes(), 0666)
|
||||||
|
if err != nil {
|
||||||
|
Errorf("WriteFile %s failed: %v", fname, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fname
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -645,7 +645,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
|
||||||
sliceSym(pcln.funcnametab)
|
sliceSym(pcln.funcnametab)
|
||||||
|
|
||||||
// The cutab slice
|
// The cutab slice
|
||||||
sliceSym(pcln.cutab)
|
slice(pcln.cutab, uint64(ldr.SymSize(pcln.cutab))/4)
|
||||||
|
|
||||||
// The filetab slice
|
// The filetab slice
|
||||||
sliceSym(pcln.filetab)
|
sliceSym(pcln.filetab)
|
||||||
|
|
@ -654,7 +654,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
|
||||||
sliceSym(pcln.pctab)
|
sliceSym(pcln.pctab)
|
||||||
|
|
||||||
// The pclntab slice
|
// The pclntab slice
|
||||||
slice(pcln.pclntab, uint64(ldr.SymSize(pcln.pclntab)))
|
sliceSym(pcln.pclntab)
|
||||||
|
|
||||||
// The ftab slice
|
// The ftab slice
|
||||||
slice(pcln.pclntab, uint64(pcln.nfunc+1))
|
slice(pcln.pclntab, uint64(pcln.nfunc+1))
|
||||||
|
|
|
||||||
|
|
@ -1779,10 +1779,7 @@ func xcoffCreateExportFile(ctxt *Link) (fname string) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
ldr := ctxt.loader
|
ldr := ctxt.loader
|
||||||
for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
|
for s := range ldr.ForAllCgoExportStatic() {
|
||||||
if !ldr.AttrCgoExport(s) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
extname := ldr.SymExtname(s)
|
extname := ldr.SymExtname(s)
|
||||||
if !strings.HasPrefix(extname, "._cgoexp_") {
|
if !strings.HasPrefix(extname, "._cgoexp_") {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"internal/abi"
|
"internal/abi"
|
||||||
"io"
|
"io"
|
||||||
|
"iter"
|
||||||
"log"
|
"log"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -1109,6 +1110,18 @@ func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForAllCgoExportStatic returns an iterator over all symbols
|
||||||
|
// marked with the "cgo_export_static" compiler directive.
|
||||||
|
func (l *Loader) ForAllCgoExportStatic() iter.Seq[Sym] {
|
||||||
|
return func(yield func(Sym) bool) {
|
||||||
|
for s := range l.attrCgoExportStatic {
|
||||||
|
if !yield(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsGeneratedSym returns true if a symbol's been previously marked as a
|
// IsGeneratedSym returns true if a symbol's been previously marked as a
|
||||||
// generator symbol through the SetIsGeneratedSym. The functions for generator
|
// generator symbol through the SetIsGeneratedSym. The functions for generator
|
||||||
// symbols are kept in the Link context.
|
// symbols are kept in the Link context.
|
||||||
|
|
@ -2437,6 +2450,9 @@ var blockedLinknames = map[string][]string{
|
||||||
"sync_test.runtime_blockUntilEmptyCleanupQueue": {"sync_test"},
|
"sync_test.runtime_blockUntilEmptyCleanupQueue": {"sync_test"},
|
||||||
"time.runtimeIsBubbled": {"time"},
|
"time.runtimeIsBubbled": {"time"},
|
||||||
"unique.runtime_blockUntilEmptyCleanupQueue": {"unique"},
|
"unique.runtime_blockUntilEmptyCleanupQueue": {"unique"},
|
||||||
|
// Experimental features
|
||||||
|
"runtime.goroutineLeakGC": {"runtime/pprof"},
|
||||||
|
"runtime.goroutineleakcount": {"runtime/pprof"},
|
||||||
// Others
|
// Others
|
||||||
"net.newWindowsFile": {"net"}, // pushed from os
|
"net.newWindowsFile": {"net"}, // pushed from os
|
||||||
"testing/synctest.testingSynctestTest": {"testing/synctest"}, // pushed from testing
|
"testing/synctest.testingSynctestTest": {"testing/synctest"}, // pushed from testing
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type MakeHash func() hash.Hash
|
||||||
// TestHash performs a set of tests on hash.Hash implementations, checking the
|
// TestHash performs a set of tests on hash.Hash implementations, checking the
|
||||||
// documented requirements of Write, Sum, Reset, Size, and BlockSize.
|
// documented requirements of Write, Sum, Reset, Size, and BlockSize.
|
||||||
func TestHash(t *testing.T, mh MakeHash) {
|
func TestHash(t *testing.T, mh MakeHash) {
|
||||||
if boring.Enabled || fips140.Version() == "v1.0" {
|
if boring.Enabled || fips140.Version() == "v1.0.0" {
|
||||||
testhash.TestHashWithoutClone(t, testhash.MakeHash(mh))
|
testhash.TestHashWithoutClone(t, testhash.MakeHash(mh))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package entropy provides the passive entropy source for the FIPS 140-3
|
// Package entropy provides the passive entropy source for the FIPS 140-3
|
||||||
// module. It is only used in FIPS mode by [crypto/internal/fips140/drbg.Read].
|
// module. It is only used in FIPS mode by [crypto/internal/fips140/drbg.Read]
|
||||||
|
// from the FIPS 140-3 Go Cryptographic Module v1.0.0. Later versions of the
|
||||||
|
// module have an internal CPU jitter-based entropy source.
|
||||||
//
|
//
|
||||||
// This complies with IG 9.3.A, Additional Comment 12, which until January 1,
|
// This complied with IG 9.3.A, Additional Comment 12, which until January 1,
|
||||||
// 2026 allows new modules to meet an [earlier version] of Resolution 2(b):
|
// 2026 allows new modules to meet an [earlier version] of Resolution 2(b):
|
||||||
// "A software module that contains an approved DRBG that receives a LOAD
|
// "A software module that contains an approved DRBG that receives a LOAD
|
||||||
// command (or its logical equivalent) with entropy obtained from [...] inside
|
// command (or its logical equivalent) with entropy obtained from [...] inside
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,10 @@ func CAST(name string, f func() error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PCT runs the named Pairwise Consistency Test (if operated in FIPS mode) and
|
// PCT runs the named Pairwise Consistency Test (if operated in FIPS mode) and
|
||||||
// returns any errors. If an error is returned, the key must not be used.
|
// aborts the program (stopping the module input/output and entering the "error
|
||||||
|
// state") if the test fails.
|
||||||
//
|
//
|
||||||
// PCTs are mandatory for every key pair that is generated/imported, including
|
// PCTs are mandatory for every generated (but not imported) key pair, including
|
||||||
// ephemeral keys (which effectively doubles the cost of key establishment). See
|
// ephemeral keys (which effectively doubles the cost of key establishment). See
|
||||||
// Implementation Guidance 10.3.A Additional Comment 1.
|
// Implementation Guidance 10.3.A Additional Comment 1.
|
||||||
//
|
//
|
||||||
|
|
@ -66,17 +67,23 @@ func CAST(name string, f func() error) {
|
||||||
//
|
//
|
||||||
// If a package p calls PCT during key generation, an invocation of that
|
// If a package p calls PCT during key generation, an invocation of that
|
||||||
// function should be added to fipstest.TestConditionals.
|
// function should be added to fipstest.TestConditionals.
|
||||||
func PCT(name string, f func() error) error {
|
func PCT(name string, f func() error) {
|
||||||
if strings.ContainsAny(name, ",#=:") {
|
if strings.ContainsAny(name, ",#=:") {
|
||||||
panic("fips: invalid self-test name: " + name)
|
panic("fips: invalid self-test name: " + name)
|
||||||
}
|
}
|
||||||
if !Enabled {
|
if !Enabled {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := f()
|
err := f()
|
||||||
if name == failfipscast {
|
if name == failfipscast {
|
||||||
err = errors.New("simulated PCT failure")
|
err = errors.New("simulated PCT failure")
|
||||||
}
|
}
|
||||||
return err
|
if err != nil {
|
||||||
|
fatal("FIPS 140-3 self-test failed: " + name + ": " + err.Error())
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
if debug {
|
||||||
|
println("FIPS 140-3 PCT passed:", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,53 @@
|
||||||
package drbg
|
package drbg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/internal/entropy"
|
|
||||||
"crypto/internal/fips140"
|
"crypto/internal/fips140"
|
||||||
|
"crypto/internal/fips140/entropy"
|
||||||
"crypto/internal/randutil"
|
"crypto/internal/randutil"
|
||||||
"crypto/internal/sysrand"
|
"crypto/internal/sysrand"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
var drbgs = sync.Pool{
|
// memory is a scratch buffer that is accessed between samples by the entropy
|
||||||
|
// source to expose it to memory access timings.
|
||||||
|
//
|
||||||
|
// We reuse it and share it between Seed calls to avoid the significant (~500µs)
|
||||||
|
// cost of zeroing a new allocation every time. The entropy source accesses it
|
||||||
|
// using atomics (and doesn't care about its contents).
|
||||||
|
//
|
||||||
|
// It should end up in the .noptrbss section, and become backed by physical pages
|
||||||
|
// at first use. This ensures that programs that do not use the FIPS 140-3 module
|
||||||
|
// do not incur any memory use or initialization penalties.
|
||||||
|
var memory entropy.ScratchBuffer
|
||||||
|
|
||||||
|
func getEntropy() *[SeedSize]byte {
|
||||||
|
var retries int
|
||||||
|
seed, err := entropy.Seed(&memory)
|
||||||
|
for err != nil {
|
||||||
|
// The CPU jitter-based SP 800-90B entropy source has a non-negligible
|
||||||
|
// chance of failing the startup health tests.
|
||||||
|
//
|
||||||
|
// Each time it does, it enters a permanent failure state, and we
|
||||||
|
// restart it anew. This is not expected to happen more than a few times
|
||||||
|
// in a row.
|
||||||
|
if retries++; retries > 100 {
|
||||||
|
panic("fips140/drbg: failed to obtain initial entropy")
|
||||||
|
}
|
||||||
|
seed, err = entropy.Seed(&memory)
|
||||||
|
}
|
||||||
|
return &seed
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEntropy is very slow (~500µs), so we don't want it on the hot path.
|
||||||
|
// We keep both a persistent DRBG instance and a pool of additional instances.
|
||||||
|
// Occasional uses will use drbgInstance, even if the pool was emptied since the
|
||||||
|
// last use. Frequent concurrent uses will fill the pool and use it.
|
||||||
|
var drbgInstance atomic.Pointer[Counter]
|
||||||
|
var drbgPool = sync.Pool{
|
||||||
New: func() any {
|
New: func() any {
|
||||||
var c *Counter
|
return NewCounter(getEntropy())
|
||||||
entropy.Depleted(func(seed *[48]byte) {
|
|
||||||
c = NewCounter(seed)
|
|
||||||
})
|
|
||||||
return c
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,8 +76,15 @@ func Read(b []byte) {
|
||||||
additionalInput := new([SeedSize]byte)
|
additionalInput := new([SeedSize]byte)
|
||||||
sysrand.Read(additionalInput[:16])
|
sysrand.Read(additionalInput[:16])
|
||||||
|
|
||||||
drbg := drbgs.Get().(*Counter)
|
drbg := drbgInstance.Swap(nil)
|
||||||
defer drbgs.Put(drbg)
|
if drbg == nil {
|
||||||
|
drbg = drbgPool.Get().(*Counter)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if !drbgInstance.CompareAndSwap(nil, drbg) {
|
||||||
|
drbgPool.Put(drbg)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
size := min(len(b), maxRequestSize)
|
size := min(len(b), maxRequestSize)
|
||||||
|
|
@ -54,9 +93,7 @@ func Read(b []byte) {
|
||||||
// Section 9.3.2: if Generate reports a reseed is required, the
|
// Section 9.3.2: if Generate reports a reseed is required, the
|
||||||
// additional input is passed to Reseed along with the entropy and
|
// additional input is passed to Reseed along with the entropy and
|
||||||
// then nulled before the next Generate call.
|
// then nulled before the next Generate call.
|
||||||
entropy.Depleted(func(seed *[48]byte) {
|
drbg.Reseed(getEntropy(), additionalInput)
|
||||||
drbg.Reseed(seed, additionalInput)
|
|
||||||
})
|
|
||||||
additionalInput = nil
|
additionalInput = nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,27 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A "Pairwise Consistency Test" makes no sense if we just generated the
|
||||||
|
// public key from an ephemeral private key. Moreover, there is no way to
|
||||||
|
// check it aside from redoing the exact same computation again. SP 800-56A
|
||||||
|
// Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it.
|
||||||
|
// However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a
|
||||||
|
// PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional
|
||||||
|
// Comment 1 goes out of its way to say that "the PCT shall be performed
|
||||||
|
// consistent [...], even if the underlying standard does not require a
|
||||||
|
// PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
|
||||||
|
fips140.PCT("ECDH PCT", func() error {
|
||||||
|
p1, err := c.newPoint().ScalarBaseMult(privateKey.d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(p1.Bytes(), privateKey.pub.q) {
|
||||||
|
return errors.New("crypto/ecdh: public key does not match private key")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
return privateKey, nil
|
return privateKey, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,28 +209,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {
|
||||||
panic("crypto/ecdh: internal error: public key is the identity element")
|
panic("crypto/ecdh: internal error: public key is the identity element")
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "Pairwise Consistency Test" makes no sense if we just generated the
|
|
||||||
// public key from an ephemeral private key. Moreover, there is no way to
|
|
||||||
// check it aside from redoing the exact same computation again. SP 800-56A
|
|
||||||
// Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it.
|
|
||||||
// However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a
|
|
||||||
// PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional
|
|
||||||
// Comment 1 goes out of its way to say that "the PCT shall be performed
|
|
||||||
// consistent [...], even if the underlying standard does not require a
|
|
||||||
// PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
|
|
||||||
if err := fips140.PCT("ECDH PCT", func() error {
|
|
||||||
p1, err := c.newPoint().ScalarBaseMult(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !bytes.Equal(p1.Bytes(), publicKey) {
|
|
||||||
return errors.New("crypto/ecdh: public key does not match private key")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
|
k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
|
||||||
return k, nil
|
return k, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ func testHash() []byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error {
|
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) {
|
||||||
return fips140.PCT("ECDSA PCT", func() error {
|
fips140.PCT("ECDSA PCT", func() error {
|
||||||
hash := testHash()
|
hash := testHash()
|
||||||
drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil)
|
drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil)
|
||||||
sig, err := sign(c, k, drbg, hash)
|
sig, err := sign(c, k, drbg, hash)
|
||||||
|
|
|
||||||
|
|
@ -167,11 +167,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)}
|
priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)}
|
||||||
if err := fipsPCT(c, priv); err != nil {
|
|
||||||
// This can happen if the application went out of its way to make an
|
|
||||||
// ecdsa.PrivateKey with a mismatching PublicKey.
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return priv, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,10 +199,7 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||||
},
|
},
|
||||||
d: k.Bytes(c.N),
|
d: k.Bytes(c.N),
|
||||||
}
|
}
|
||||||
if err := fipsPCT(c, priv); err != nil {
|
fipsPCT(c, priv)
|
||||||
// This clearly can't happen, but FIPS 140-3 mandates that we check it.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return priv, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ func newDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s personalizatio
|
||||||
//
|
//
|
||||||
// This should only be used for ACVP testing. hmacDRBG is not intended to be
|
// This should only be used for ACVP testing. hmacDRBG is not intended to be
|
||||||
// used directly.
|
// used directly.
|
||||||
func TestingOnlyNewDRBG(hash func() hash.Hash, entropy, nonce []byte, s []byte) *hmacDRBG {
|
func TestingOnlyNewDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s []byte) *hmacDRBG {
|
||||||
return newDRBG(hash, entropy, nonce, plainPersonalizationString(s))
|
return newDRBG(hash, entropy, nonce, plainPersonalizationString(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fipsPCT(k *PrivateKey) error {
|
func fipsPCT(k *PrivateKey) {
|
||||||
return fips140.PCT("Ed25519 sign and verify PCT", func() error {
|
fips140.PCT("Ed25519 sign and verify PCT", func() error {
|
||||||
return pairwiseTest(k)
|
return pairwiseTest(k)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,7 @@ func generateKey(priv *PrivateKey) (*PrivateKey, error) {
|
||||||
fips140.RecordApproved()
|
fips140.RecordApproved()
|
||||||
drbg.Read(priv.seed[:])
|
drbg.Read(priv.seed[:])
|
||||||
precomputePrivateKey(priv)
|
precomputePrivateKey(priv)
|
||||||
if err := fipsPCT(priv); err != nil {
|
fipsPCT(priv)
|
||||||
// This clearly can't happen, but FIPS 140-3 requires that we check.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return priv, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,10 +85,6 @@ func newPrivateKeyFromSeed(priv *PrivateKey, seed []byte) (*PrivateKey, error) {
|
||||||
}
|
}
|
||||||
copy(priv.seed[:], seed)
|
copy(priv.seed[:], seed)
|
||||||
precomputePrivateKey(priv)
|
precomputePrivateKey(priv)
|
||||||
if err := fipsPCT(priv); err != nil {
|
|
||||||
// This clearly can't happen, but FIPS 140-3 requires that we check.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return priv, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,12 +130,6 @@ func newPrivateKey(priv *PrivateKey, privBytes []byte) (*PrivateKey, error) {
|
||||||
|
|
||||||
copy(priv.prefix[:], h[32:])
|
copy(priv.prefix[:], h[32:])
|
||||||
|
|
||||||
if err := fipsPCT(priv); err != nil {
|
|
||||||
// This can happen if the application messed with the private key
|
|
||||||
// encoding, and the public key doesn't match the seed anymore.
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return priv, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
202
src/crypto/internal/fips140/entropy/entropy.go
Normal file
202
src/crypto/internal/fips140/entropy/entropy.go
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
// 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 entropy implements a CPU jitter-based SP 800-90B entropy source.
|
||||||
|
package entropy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/internal/fips140deps/time"
|
||||||
|
"errors"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version returns the version of the entropy source.
|
||||||
|
//
|
||||||
|
// This is independent of the FIPS 140-3 module version, in order to reuse the
|
||||||
|
// ESV certificate across module versions.
|
||||||
|
func Version() string {
|
||||||
|
return "v1.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScratchBuffer is a large buffer that will be written to using atomics, to
|
||||||
|
// generate noise from memory access timings. Its contents do not matter.
|
||||||
|
type ScratchBuffer [1 << 25]byte
|
||||||
|
|
||||||
|
// Seed returns a 384-bit seed with full entropy.
|
||||||
|
//
|
||||||
|
// memory is passed in to allow changing the allocation strategy without
|
||||||
|
// modifying the frozen and certified entropy source in this package.
|
||||||
|
//
|
||||||
|
// Seed returns an error if the entropy source startup health tests fail, which
|
||||||
|
// has a non-negligible chance of happening.
|
||||||
|
func Seed(memory *ScratchBuffer) ([48]byte, error) {
|
||||||
|
// Collect w = 1024 samples, each certified to provide no less than h = 0.5
|
||||||
|
// bits of entropy, for a total of hᵢₙ = w × h = 512 bits of entropy, over
|
||||||
|
// nᵢₙ = w × n = 8192 bits of input data.
|
||||||
|
var samples [1024]byte
|
||||||
|
if err := Samples(samples[:], memory); err != nil {
|
||||||
|
return [48]byte{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a vetted unkeyed conditioning component, SHA-384, with nw = 384 and
|
||||||
|
// nₒᵤₜ = 384. Per the formula in SP 800-90B Section 3.1.5.1.2, the output
|
||||||
|
// entropy hₒᵤₜ is:
|
||||||
|
//
|
||||||
|
// sage: n_in = 8192
|
||||||
|
// sage: n_out = 384
|
||||||
|
// sage: nw = 384
|
||||||
|
// sage: h_in = 512
|
||||||
|
// sage: P_high = 2^(-h_in)
|
||||||
|
// sage: P_low = (1 - P_high) / (2^n_in - 1)
|
||||||
|
// sage: n = min(n_out, nw)
|
||||||
|
// sage: ψ = 2^(n_in - n) * P_low + P_high
|
||||||
|
// sage: U = 2^(n_in - n) + sqrt(2 * n * 2^(n_in - n) * ln(2))
|
||||||
|
// sage: ω = U * P_low
|
||||||
|
// sage: h_out = -log(max(ψ, ω), 2)
|
||||||
|
// sage: h_out.n()
|
||||||
|
// 384.000000000000
|
||||||
|
//
|
||||||
|
// According to Implementation Guidance D.K, Resolution 19, since
|
||||||
|
//
|
||||||
|
// - the conditioning component is vetted,
|
||||||
|
// - hᵢₙ = 512 ≥ nₒᵤₜ + 64 = 448, and
|
||||||
|
// - nₒᵤₜ ≤ security strength of SHA-384 = 384 (per SP 800-107 Rev. 1, Table 1),
|
||||||
|
//
|
||||||
|
// we can claim the output has full entropy.
|
||||||
|
return SHA384(&samples), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples starts a new entropy source, collects the requested number of
|
||||||
|
// samples, conducts startup health tests, and returns the samples or an error
|
||||||
|
// if the health tests fail.
|
||||||
|
//
|
||||||
|
// The health tests have a non-negligible chance of failing.
|
||||||
|
func Samples(samples []uint8, memory *ScratchBuffer) error {
|
||||||
|
if len(samples) < 1024 {
|
||||||
|
return errors.New("entropy: at least 1024 samples are required for startup health tests")
|
||||||
|
}
|
||||||
|
s := newSource(memory)
|
||||||
|
for range 4 {
|
||||||
|
// Warm up the source to avoid any initial bias.
|
||||||
|
_ = s.Sample()
|
||||||
|
}
|
||||||
|
for i := range samples {
|
||||||
|
samples[i] = s.Sample()
|
||||||
|
}
|
||||||
|
if err := RepetitionCountTest(samples); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := AdaptiveProportionTest(samples); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type source struct {
|
||||||
|
memory *ScratchBuffer
|
||||||
|
lcgState uint32
|
||||||
|
previous int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSource(memory *ScratchBuffer) *source {
|
||||||
|
return &source{
|
||||||
|
memory: memory,
|
||||||
|
lcgState: uint32(time.HighPrecisionNow()),
|
||||||
|
previous: time.HighPrecisionNow(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// touchMemory performs a write to memory at the given index.
|
||||||
|
//
|
||||||
|
// The memory slice is passed in and may be shared across sources e.g. to avoid
|
||||||
|
// the significant (~500µs) cost of zeroing a new allocation on every [Seed] call.
|
||||||
|
func touchMemory(memory *ScratchBuffer, idx uint32) {
|
||||||
|
idx = idx / 4 * 4 // align to 32 bits
|
||||||
|
u32 := (*uint32)(unsafe.Pointer(&memory[idx]))
|
||||||
|
last := atomic.LoadUint32(u32)
|
||||||
|
atomic.SwapUint32(u32, last+13)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *source) Sample() uint8 {
|
||||||
|
// Perform a few memory accesses in an unpredictable pattern to expose the
|
||||||
|
// next measurement to as much system noise as possible.
|
||||||
|
memory, lcgState := s.memory, s.lcgState
|
||||||
|
_ = memory[0] // hoist the nil check out of touchMemory
|
||||||
|
for range 64 {
|
||||||
|
lcgState = 1664525*lcgState + 1013904223
|
||||||
|
// Discard the lower bits, which tend to fall into short cycles.
|
||||||
|
idx := (lcgState >> 6) & (1<<25 - 1)
|
||||||
|
touchMemory(memory, idx)
|
||||||
|
}
|
||||||
|
s.lcgState = lcgState
|
||||||
|
|
||||||
|
t := time.HighPrecisionNow()
|
||||||
|
sample := t - s.previous
|
||||||
|
s.previous = t
|
||||||
|
|
||||||
|
// Reduce the symbol space to 256 values, assuming most of the entropy is in
|
||||||
|
// the least-significant bits, which represent the highest-resolution timing
|
||||||
|
// differences.
|
||||||
|
return uint8(sample)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepetitionCountTest implements the repetition count test from SP 800-90B
|
||||||
|
// Section 4.4.1. It returns an error if any symbol is repeated C = 41 or more
|
||||||
|
// times in a row.
|
||||||
|
//
|
||||||
|
// This C value is calculated from a target failure probability α = 2⁻²⁰ and a
|
||||||
|
// claimed min-entropy per symbol h = 0.5 bits, using the formula in SP 800-90B
|
||||||
|
// Section 4.4.1.
|
||||||
|
//
|
||||||
|
// sage: α = 2^-20
|
||||||
|
// sage: H = 0.5
|
||||||
|
// sage: 1 + ceil(-log(α, 2) / H)
|
||||||
|
// 41
|
||||||
|
func RepetitionCountTest(samples []uint8) error {
|
||||||
|
x := samples[0]
|
||||||
|
count := 1
|
||||||
|
for _, y := range samples[1:] {
|
||||||
|
if y == x {
|
||||||
|
count++
|
||||||
|
if count >= 41 {
|
||||||
|
return errors.New("entropy: repetition count health test failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x = y
|
||||||
|
count = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdaptiveProportionTest implements the adaptive proportion test from SP 800-90B
|
||||||
|
// Section 4.4.2. It returns an error if any symbol appears C = 410 or more
|
||||||
|
// times in the last W = 512 samples.
|
||||||
|
//
|
||||||
|
// This C value is calculated from a target failure probability α = 2⁻²⁰, a
|
||||||
|
// window size W = 512, and a claimed min-entropy per symbol h = 0.5 bits, using
|
||||||
|
// the formula in SP 800-90B Section 4.4.2, equivalent to the Microsoft Excel
|
||||||
|
// formula 1+CRITBINOM(W, power(2,(−H)),1−α).
|
||||||
|
//
|
||||||
|
// sage: from scipy.stats import binom
|
||||||
|
// sage: α = 2^-20
|
||||||
|
// sage: H = 0.5
|
||||||
|
// sage: W = 512
|
||||||
|
// sage: C = 1 + binom.ppf(1 - α, W, 2**(-H))
|
||||||
|
// sage: ceil(C)
|
||||||
|
// 410
|
||||||
|
func AdaptiveProportionTest(samples []uint8) error {
|
||||||
|
var counts [256]int
|
||||||
|
for i, x := range samples {
|
||||||
|
counts[x]++
|
||||||
|
if i >= 512 {
|
||||||
|
counts[samples[i-512]]--
|
||||||
|
}
|
||||||
|
if counts[x] >= 410 {
|
||||||
|
return errors.New("entropy: adaptive proportion health test failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
191
src/crypto/internal/fips140/entropy/sha384.go
Normal file
191
src/crypto/internal/fips140/entropy/sha384.go
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
// 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 entropy
|
||||||
|
|
||||||
|
import "math/bits"
|
||||||
|
|
||||||
|
// This file includes a SHA-384 implementation to insulate the entropy source
|
||||||
|
// from any changes in the FIPS 140-3 module's crypto/internal/fips140/sha512
|
||||||
|
// package. We only support 1024-byte inputs.
|
||||||
|
|
||||||
|
func SHA384(p *[1024]byte) [48]byte {
|
||||||
|
h := [8]uint64{
|
||||||
|
0xcbbb9d5dc1059ed8,
|
||||||
|
0x629a292a367cd507,
|
||||||
|
0x9159015a3070dd17,
|
||||||
|
0x152fecd8f70e5939,
|
||||||
|
0x67332667ffc00b31,
|
||||||
|
0x8eb44a8768581511,
|
||||||
|
0xdb0c2e0d64f98fa7,
|
||||||
|
0x47b5481dbefa4fa4,
|
||||||
|
}
|
||||||
|
|
||||||
|
sha384Block(&h, (*[128]byte)(p[0:128]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[128:256]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[256:384]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[384:512]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[512:640]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[640:768]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[768:896]))
|
||||||
|
sha384Block(&h, (*[128]byte)(p[896:1024]))
|
||||||
|
|
||||||
|
var padlen [128]byte
|
||||||
|
padlen[0] = 0x80
|
||||||
|
bePutUint64(padlen[112+8:], 1024*8)
|
||||||
|
sha384Block(&h, &padlen)
|
||||||
|
|
||||||
|
var digest [48]byte
|
||||||
|
bePutUint64(digest[0:], h[0])
|
||||||
|
bePutUint64(digest[8:], h[1])
|
||||||
|
bePutUint64(digest[16:], h[2])
|
||||||
|
bePutUint64(digest[24:], h[3])
|
||||||
|
bePutUint64(digest[32:], h[4])
|
||||||
|
bePutUint64(digest[40:], h[5])
|
||||||
|
return digest
|
||||||
|
}
|
||||||
|
|
||||||
|
var _K = [...]uint64{
|
||||||
|
0x428a2f98d728ae22,
|
||||||
|
0x7137449123ef65cd,
|
||||||
|
0xb5c0fbcfec4d3b2f,
|
||||||
|
0xe9b5dba58189dbbc,
|
||||||
|
0x3956c25bf348b538,
|
||||||
|
0x59f111f1b605d019,
|
||||||
|
0x923f82a4af194f9b,
|
||||||
|
0xab1c5ed5da6d8118,
|
||||||
|
0xd807aa98a3030242,
|
||||||
|
0x12835b0145706fbe,
|
||||||
|
0x243185be4ee4b28c,
|
||||||
|
0x550c7dc3d5ffb4e2,
|
||||||
|
0x72be5d74f27b896f,
|
||||||
|
0x80deb1fe3b1696b1,
|
||||||
|
0x9bdc06a725c71235,
|
||||||
|
0xc19bf174cf692694,
|
||||||
|
0xe49b69c19ef14ad2,
|
||||||
|
0xefbe4786384f25e3,
|
||||||
|
0x0fc19dc68b8cd5b5,
|
||||||
|
0x240ca1cc77ac9c65,
|
||||||
|
0x2de92c6f592b0275,
|
||||||
|
0x4a7484aa6ea6e483,
|
||||||
|
0x5cb0a9dcbd41fbd4,
|
||||||
|
0x76f988da831153b5,
|
||||||
|
0x983e5152ee66dfab,
|
||||||
|
0xa831c66d2db43210,
|
||||||
|
0xb00327c898fb213f,
|
||||||
|
0xbf597fc7beef0ee4,
|
||||||
|
0xc6e00bf33da88fc2,
|
||||||
|
0xd5a79147930aa725,
|
||||||
|
0x06ca6351e003826f,
|
||||||
|
0x142929670a0e6e70,
|
||||||
|
0x27b70a8546d22ffc,
|
||||||
|
0x2e1b21385c26c926,
|
||||||
|
0x4d2c6dfc5ac42aed,
|
||||||
|
0x53380d139d95b3df,
|
||||||
|
0x650a73548baf63de,
|
||||||
|
0x766a0abb3c77b2a8,
|
||||||
|
0x81c2c92e47edaee6,
|
||||||
|
0x92722c851482353b,
|
||||||
|
0xa2bfe8a14cf10364,
|
||||||
|
0xa81a664bbc423001,
|
||||||
|
0xc24b8b70d0f89791,
|
||||||
|
0xc76c51a30654be30,
|
||||||
|
0xd192e819d6ef5218,
|
||||||
|
0xd69906245565a910,
|
||||||
|
0xf40e35855771202a,
|
||||||
|
0x106aa07032bbd1b8,
|
||||||
|
0x19a4c116b8d2d0c8,
|
||||||
|
0x1e376c085141ab53,
|
||||||
|
0x2748774cdf8eeb99,
|
||||||
|
0x34b0bcb5e19b48a8,
|
||||||
|
0x391c0cb3c5c95a63,
|
||||||
|
0x4ed8aa4ae3418acb,
|
||||||
|
0x5b9cca4f7763e373,
|
||||||
|
0x682e6ff3d6b2b8a3,
|
||||||
|
0x748f82ee5defb2fc,
|
||||||
|
0x78a5636f43172f60,
|
||||||
|
0x84c87814a1f0ab72,
|
||||||
|
0x8cc702081a6439ec,
|
||||||
|
0x90befffa23631e28,
|
||||||
|
0xa4506cebde82bde9,
|
||||||
|
0xbef9a3f7b2c67915,
|
||||||
|
0xc67178f2e372532b,
|
||||||
|
0xca273eceea26619c,
|
||||||
|
0xd186b8c721c0c207,
|
||||||
|
0xeada7dd6cde0eb1e,
|
||||||
|
0xf57d4f7fee6ed178,
|
||||||
|
0x06f067aa72176fba,
|
||||||
|
0x0a637dc5a2c898a6,
|
||||||
|
0x113f9804bef90dae,
|
||||||
|
0x1b710b35131c471b,
|
||||||
|
0x28db77f523047d84,
|
||||||
|
0x32caab7b40c72493,
|
||||||
|
0x3c9ebe0a15c9bebc,
|
||||||
|
0x431d67c49c100d4c,
|
||||||
|
0x4cc5d4becb3e42b6,
|
||||||
|
0x597f299cfc657e2a,
|
||||||
|
0x5fcb6fab3ad6faec,
|
||||||
|
0x6c44198c4a475817,
|
||||||
|
}
|
||||||
|
|
||||||
|
func sha384Block(dh *[8]uint64, p *[128]byte) {
|
||||||
|
var w [80]uint64
|
||||||
|
for i := range 80 {
|
||||||
|
if i < 16 {
|
||||||
|
w[i] = beUint64(p[i*8:])
|
||||||
|
} else {
|
||||||
|
v1 := w[i-2]
|
||||||
|
t1 := bits.RotateLeft64(v1, -19) ^ bits.RotateLeft64(v1, -61) ^ (v1 >> 6)
|
||||||
|
v2 := w[i-15]
|
||||||
|
t2 := bits.RotateLeft64(v2, -1) ^ bits.RotateLeft64(v2, -8) ^ (v2 >> 7)
|
||||||
|
|
||||||
|
w[i] = t1 + w[i-7] + t2 + w[i-16]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a, b, c, d, e, f, g, h := dh[0], dh[1], dh[2], dh[3], dh[4], dh[5], dh[6], dh[7]
|
||||||
|
|
||||||
|
for i := range 80 {
|
||||||
|
t1 := h + (bits.RotateLeft64(e, -14) ^ bits.RotateLeft64(e, -18) ^
|
||||||
|
bits.RotateLeft64(e, -41)) + ((e & f) ^ (^e & g)) + _K[i] + w[i]
|
||||||
|
t2 := (bits.RotateLeft64(a, -28) ^ bits.RotateLeft64(a, -34) ^
|
||||||
|
bits.RotateLeft64(a, -39)) + ((a & b) ^ (a & c) ^ (b & c))
|
||||||
|
|
||||||
|
h = g
|
||||||
|
g = f
|
||||||
|
f = e
|
||||||
|
e = d + t1
|
||||||
|
d = c
|
||||||
|
c = b
|
||||||
|
b = a
|
||||||
|
a = t1 + t2
|
||||||
|
}
|
||||||
|
|
||||||
|
dh[0] += a
|
||||||
|
dh[1] += b
|
||||||
|
dh[2] += c
|
||||||
|
dh[3] += d
|
||||||
|
dh[4] += e
|
||||||
|
dh[5] += f
|
||||||
|
dh[6] += g
|
||||||
|
dh[7] += h
|
||||||
|
}
|
||||||
|
|
||||||
|
func beUint64(b []byte) uint64 {
|
||||||
|
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||||
|
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||||
|
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||||
|
}
|
||||||
|
|
||||||
|
func bePutUint64(b []byte, v uint64) {
|
||||||
|
_ = b[7] // early bounds check to guarantee safety of writes below
|
||||||
|
b[0] = byte(v >> 56)
|
||||||
|
b[1] = byte(v >> 48)
|
||||||
|
b[2] = byte(v >> 40)
|
||||||
|
b[3] = byte(v >> 32)
|
||||||
|
b[4] = byte(v >> 24)
|
||||||
|
b[5] = byte(v >> 16)
|
||||||
|
b[6] = byte(v >> 8)
|
||||||
|
b[7] = byte(v)
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ package fips140
|
||||||
import (
|
import (
|
||||||
"crypto/internal/fips140deps/godebug"
|
"crypto/internal/fips140deps/godebug"
|
||||||
"errors"
|
"errors"
|
||||||
"hash"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -49,6 +48,8 @@ func Supported() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See EnableFIPS in cmd/internal/obj/fips.go for commentary.
|
// See EnableFIPS in cmd/internal/obj/fips.go for commentary.
|
||||||
|
// Also, js/wasm and windows/386 don't have good enough timers
|
||||||
|
// for the CPU jitter entropy source.
|
||||||
switch {
|
switch {
|
||||||
case runtime.GOARCH == "wasm",
|
case runtime.GOARCH == "wasm",
|
||||||
runtime.GOOS == "windows" && runtime.GOARCH == "386",
|
runtime.GOOS == "windows" && runtime.GOARCH == "386",
|
||||||
|
|
@ -68,16 +69,10 @@ func Name() string {
|
||||||
return "Go Cryptographic Module"
|
return "Go Cryptographic Module"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version returns the formal version (such as "v1.0") if building against a
|
// Version returns the formal version (such as "v1.0.0") if building against a
|
||||||
// frozen module with GOFIPS140. Otherwise, it returns "latest".
|
// frozen module with GOFIPS140. Otherwise, it returns "latest".
|
||||||
func Version() string {
|
func Version() string {
|
||||||
// This return value is replaced by mkzip.go, it must not be changed or
|
// This return value is replaced by mkzip.go, it must not be changed or
|
||||||
// moved to a different file.
|
// moved to a different file.
|
||||||
return "latest" //mkzip:version
|
return "latest" //mkzip:version
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash is a legacy compatibility alias for hash.Hash.
|
|
||||||
//
|
|
||||||
// It's only here because [crypto/internal/fips140/ecdsa.TestingOnlyNewDRBG]
|
|
||||||
// takes a "func() fips140.Hash" in v1.0.0, instead of being generic.
|
|
||||||
type Hash = hash.Hash
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@ import (
|
||||||
"crypto/internal/fips140"
|
"crypto/internal/fips140"
|
||||||
_ "crypto/internal/fips140/check"
|
_ "crypto/internal/fips140/check"
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
var fipsSelfTest = sync.OnceFunc(func() {
|
||||||
fips140.CAST("ML-KEM-768", func() error {
|
fips140.CAST("ML-KEM-768", func() error {
|
||||||
var d = &[32]byte{
|
var d = &[32]byte{
|
||||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
|
@ -40,14 +41,12 @@ func init() {
|
||||||
dk := &DecapsulationKey768{}
|
dk := &DecapsulationKey768{}
|
||||||
kemKeyGen(dk, d, z)
|
kemKeyGen(dk, d, z)
|
||||||
ek := dk.EncapsulationKey()
|
ek := dk.EncapsulationKey()
|
||||||
Ke, c := ek.EncapsulateInternal(m)
|
var cc [CiphertextSize768]byte
|
||||||
Kd, err := dk.Decapsulate(c)
|
Ke, _ := kemEncaps(&cc, ek, m)
|
||||||
if err != nil {
|
Kd := kemDecaps(dk, &cc)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !bytes.Equal(Ke, K) || !bytes.Equal(Kd, K) {
|
if !bytes.Equal(Ke, K) || !bytes.Equal(Kd, K) {
|
||||||
return errors.New("unexpected result")
|
return errors.New("unexpected result")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
|
|
||||||
|
|
@ -113,15 +113,13 @@ func GenerateKey1024() (*DecapsulationKey1024, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) {
|
func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) {
|
||||||
|
fipsSelfTest()
|
||||||
var d [32]byte
|
var d [32]byte
|
||||||
drbg.Read(d[:])
|
drbg.Read(d[:])
|
||||||
var z [32]byte
|
var z [32]byte
|
||||||
drbg.Read(z[:])
|
drbg.Read(z[:])
|
||||||
kemKeyGen1024(dk, &d, &z)
|
kemKeyGen1024(dk, &d, &z)
|
||||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }); err != nil {
|
fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) })
|
||||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fips140.RecordApproved()
|
fips140.RecordApproved()
|
||||||
return dk, nil
|
return dk, nil
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +127,7 @@ func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) {
|
||||||
// GenerateKeyInternal1024 is a derandomized version of GenerateKey1024,
|
// GenerateKeyInternal1024 is a derandomized version of GenerateKey1024,
|
||||||
// exclusively for use in tests.
|
// exclusively for use in tests.
|
||||||
func GenerateKeyInternal1024(d, z *[32]byte) *DecapsulationKey1024 {
|
func GenerateKeyInternal1024(d, z *[32]byte) *DecapsulationKey1024 {
|
||||||
|
fipsSelfTest()
|
||||||
dk := &DecapsulationKey1024{}
|
dk := &DecapsulationKey1024{}
|
||||||
kemKeyGen1024(dk, d, z)
|
kemKeyGen1024(dk, d, z)
|
||||||
return dk
|
return dk
|
||||||
|
|
@ -149,10 +148,6 @@ func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKe
|
||||||
d := (*[32]byte)(seed[:32])
|
d := (*[32]byte)(seed[:32])
|
||||||
z := (*[32]byte)(seed[32:])
|
z := (*[32]byte)(seed[32:])
|
||||||
kemKeyGen1024(dk, d, z)
|
kemKeyGen1024(dk, d, z)
|
||||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }); err != nil {
|
|
||||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fips140.RecordApproved()
|
fips140.RecordApproved()
|
||||||
return dk, nil
|
return dk, nil
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +280,7 @@ func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (sharedKey, ciphertext []byte) {
|
func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (sharedKey, ciphertext []byte) {
|
||||||
|
fipsSelfTest()
|
||||||
var m [messageSize]byte
|
var m [messageSize]byte
|
||||||
drbg.Read(m[:])
|
drbg.Read(m[:])
|
||||||
// Note that the modulus check (step 2 of the encapsulation key check from
|
// Note that the modulus check (step 2 of the encapsulation key check from
|
||||||
|
|
@ -296,6 +292,7 @@ func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (share
|
||||||
// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
|
// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
|
||||||
// use in tests.
|
// use in tests.
|
||||||
func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) {
|
func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) {
|
||||||
|
fipsSelfTest()
|
||||||
cc := &[CiphertextSize1024]byte{}
|
cc := &[CiphertextSize1024]byte{}
|
||||||
return kemEncaps1024(cc, ek, m)
|
return kemEncaps1024(cc, ek, m)
|
||||||
}
|
}
|
||||||
|
|
@ -401,6 +398,7 @@ func pkeEncrypt1024(cc *[CiphertextSize1024]byte, ex *encryptionKey1024, m *[mes
|
||||||
//
|
//
|
||||||
// The shared key must be kept secret.
|
// The shared key must be kept secret.
|
||||||
func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
|
func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
|
||||||
|
fipsSelfTest()
|
||||||
if len(ciphertext) != CiphertextSize1024 {
|
if len(ciphertext) != CiphertextSize1024 {
|
||||||
return nil, errors.New("mlkem: invalid ciphertext length")
|
return nil, errors.New("mlkem: invalid ciphertext length")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,15 +172,13 @@ func GenerateKey768() (*DecapsulationKey768, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) {
|
func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) {
|
||||||
|
fipsSelfTest()
|
||||||
var d [32]byte
|
var d [32]byte
|
||||||
drbg.Read(d[:])
|
drbg.Read(d[:])
|
||||||
var z [32]byte
|
var z [32]byte
|
||||||
drbg.Read(z[:])
|
drbg.Read(z[:])
|
||||||
kemKeyGen(dk, &d, &z)
|
kemKeyGen(dk, &d, &z)
|
||||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil {
|
fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) })
|
||||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fips140.RecordApproved()
|
fips140.RecordApproved()
|
||||||
return dk, nil
|
return dk, nil
|
||||||
}
|
}
|
||||||
|
|
@ -188,6 +186,7 @@ func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) {
|
||||||
// GenerateKeyInternal768 is a derandomized version of GenerateKey768,
|
// GenerateKeyInternal768 is a derandomized version of GenerateKey768,
|
||||||
// exclusively for use in tests.
|
// exclusively for use in tests.
|
||||||
func GenerateKeyInternal768(d, z *[32]byte) *DecapsulationKey768 {
|
func GenerateKeyInternal768(d, z *[32]byte) *DecapsulationKey768 {
|
||||||
|
fipsSelfTest()
|
||||||
dk := &DecapsulationKey768{}
|
dk := &DecapsulationKey768{}
|
||||||
kemKeyGen(dk, d, z)
|
kemKeyGen(dk, d, z)
|
||||||
return dk
|
return dk
|
||||||
|
|
@ -208,10 +207,6 @@ func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768,
|
||||||
d := (*[32]byte)(seed[:32])
|
d := (*[32]byte)(seed[:32])
|
||||||
z := (*[32]byte)(seed[32:])
|
z := (*[32]byte)(seed[32:])
|
||||||
kemKeyGen(dk, d, z)
|
kemKeyGen(dk, d, z)
|
||||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil {
|
|
||||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fips140.RecordApproved()
|
fips140.RecordApproved()
|
||||||
return dk, nil
|
return dk, nil
|
||||||
}
|
}
|
||||||
|
|
@ -344,6 +339,7 @@ func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (sharedKey, ciphertext []byte) {
|
func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (sharedKey, ciphertext []byte) {
|
||||||
|
fipsSelfTest()
|
||||||
var m [messageSize]byte
|
var m [messageSize]byte
|
||||||
drbg.Read(m[:])
|
drbg.Read(m[:])
|
||||||
// Note that the modulus check (step 2 of the encapsulation key check from
|
// Note that the modulus check (step 2 of the encapsulation key check from
|
||||||
|
|
@ -355,6 +351,7 @@ func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (sharedK
|
||||||
// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
|
// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
|
||||||
// use in tests.
|
// use in tests.
|
||||||
func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) {
|
func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) {
|
||||||
|
fipsSelfTest()
|
||||||
cc := &[CiphertextSize768]byte{}
|
cc := &[CiphertextSize768]byte{}
|
||||||
return kemEncaps(cc, ek, m)
|
return kemEncaps(cc, ek, m)
|
||||||
}
|
}
|
||||||
|
|
@ -460,6 +457,7 @@ func pkeEncrypt(cc *[CiphertextSize768]byte, ex *encryptionKey, m *[messageSize]
|
||||||
//
|
//
|
||||||
// The shared key must be kept secret.
|
// The shared key must be kept secret.
|
||||||
func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
|
func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
|
||||||
|
fipsSelfTest()
|
||||||
if len(ciphertext) != CiphertextSize768 {
|
if len(ciphertext) != CiphertextSize768 {
|
||||||
return nil, errors.New("mlkem: invalid ciphertext length")
|
return nil, errors.New("mlkem: invalid ciphertext length")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,28 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
|
||||||
// negligible chance of failure we can defer the check to the end of key
|
// negligible chance of failure we can defer the check to the end of key
|
||||||
// generation and return an error if it fails. See [checkPrivateKey].
|
// generation and return an error if it fails. See [checkPrivateKey].
|
||||||
|
|
||||||
return newPrivateKey(N, 65537, d, P, Q)
|
k, err := newPrivateKey(N, 65537, d, P, Q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.fipsApproved {
|
||||||
|
fips140.PCT("RSA sign and verify PCT", func() error {
|
||||||
|
hash := []byte{
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||||
|
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||||
|
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||||
|
}
|
||||||
|
sig, err := signPKCS1v15(k, "SHA-256", hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return verifyPKCS1v15(k.PublicKey(), "SHA-256", hash, sig)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return k, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,26 +320,6 @@ func checkPrivateKey(priv *PrivateKey) error {
|
||||||
return errors.New("crypto/rsa: d too small")
|
return errors.New("crypto/rsa: d too small")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the key is still in scope for FIPS mode, perform a Pairwise
|
|
||||||
// Consistency Test.
|
|
||||||
if priv.fipsApproved {
|
|
||||||
if err := fips140.PCT("RSA sign and verify PCT", func() error {
|
|
||||||
hash := []byte{
|
|
||||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
||||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
|
||||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
|
||||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
|
||||||
}
|
|
||||||
sig, err := signPKCS1v15(priv, "SHA-256", hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return verifyPKCS1v15(priv.PublicKey(), "SHA-256", hash, sig)
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ func TestImports(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that all packages except check and check's dependencies import check.
|
// Ensure that all packages except check, check's dependencies, and the
|
||||||
|
// entropy source (which is used only from .../fips140/drbg) import check.
|
||||||
for pkg := range allPackages {
|
for pkg := range allPackages {
|
||||||
switch pkg {
|
switch pkg {
|
||||||
case "crypto/internal/fips140/check":
|
case "crypto/internal/fips140/check":
|
||||||
|
|
@ -99,6 +100,7 @@ func TestImports(t *testing.T) {
|
||||||
case "crypto/internal/fips140/sha3":
|
case "crypto/internal/fips140/sha3":
|
||||||
case "crypto/internal/fips140/sha256":
|
case "crypto/internal/fips140/sha256":
|
||||||
case "crypto/internal/fips140/sha512":
|
case "crypto/internal/fips140/sha512":
|
||||||
|
case "crypto/internal/fips140/entropy":
|
||||||
default:
|
default:
|
||||||
if !importCheck[pkg] {
|
if !importCheck[pkg] {
|
||||||
t.Errorf("package %s does not import crypto/internal/fips140/check", pkg)
|
t.Errorf("package %s does not import crypto/internal/fips140/check", pkg)
|
||||||
|
|
|
||||||
21
src/crypto/internal/fips140deps/time/time.go
Normal file
21
src/crypto/internal/fips140deps/time/time.go
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package time
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
var start = time.Now()
|
||||||
|
|
||||||
|
// HighPrecisionNow returns a high-resolution timestamp suitable for measuring
|
||||||
|
// small time differences. It uses the time package's monotonic clock.
|
||||||
|
//
|
||||||
|
// Its unit, epoch, and resolution are unspecified, and may change, but can be
|
||||||
|
// assumed to be sufficiently precise to measure time differences on the order
|
||||||
|
// of tens to hundreds of nanoseconds.
|
||||||
|
func HighPrecisionNow() int64 {
|
||||||
|
return int64(time.Since(start))
|
||||||
|
}
|
||||||
17
src/crypto/internal/fips140deps/time/time_windows.go
Normal file
17
src/crypto/internal/fips140deps/time/time_windows.go
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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 time
|
||||||
|
|
||||||
|
import "internal/syscall/windows"
|
||||||
|
|
||||||
|
// HighPrecisionNow returns a high-resolution timestamp suitable for measuring
|
||||||
|
// small time differences. It uses Windows' QueryPerformanceCounter.
|
||||||
|
//
|
||||||
|
// Its unit, epoch, and resolution are unspecified, and may change, but can be
|
||||||
|
// assumed to be sufficiently precise to measure time differences on the order
|
||||||
|
// of tens to hundreds of nanoseconds.
|
||||||
|
func HighPrecisionNow() int64 {
|
||||||
|
return windows.QueryPerformanceCounter()
|
||||||
|
}
|
||||||
|
|
@ -1624,7 +1624,7 @@ func cmdHmacDrbgAft(h func() hash.Hash) command {
|
||||||
// * Uninstantiate
|
// * Uninstantiate
|
||||||
// See Table 7 in draft-vassilev-acvp-drbg
|
// See Table 7 in draft-vassilev-acvp-drbg
|
||||||
out := make([]byte, outLen)
|
out := make([]byte, outLen)
|
||||||
drbg := ecdsa.TestingOnlyNewDRBG(func() fips140.Hash { return h() }, entropy, nonce, personalization)
|
drbg := ecdsa.TestingOnlyNewDRBG(h, entropy, nonce, personalization)
|
||||||
drbg.Generate(out)
|
drbg.Generate(out)
|
||||||
drbg.Generate(out)
|
drbg.Generate(out)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@
|
||||||
package fipstest
|
package fipstest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
@ -18,6 +17,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
// Import packages that define CASTs to test them.
|
// Import packages that define CASTs to test them.
|
||||||
|
"crypto/internal/cryptotest"
|
||||||
_ "crypto/internal/fips140/aes"
|
_ "crypto/internal/fips140/aes"
|
||||||
_ "crypto/internal/fips140/aes/gcm"
|
_ "crypto/internal/fips140/aes/gcm"
|
||||||
_ "crypto/internal/fips140/drbg"
|
_ "crypto/internal/fips140/drbg"
|
||||||
|
|
@ -48,10 +48,8 @@ var allCASTs = []string{
|
||||||
"HKDF-SHA2-256",
|
"HKDF-SHA2-256",
|
||||||
"HMAC-SHA2-256",
|
"HMAC-SHA2-256",
|
||||||
"KAS-ECC-SSC P-256",
|
"KAS-ECC-SSC P-256",
|
||||||
"ML-KEM PCT",
|
"ML-KEM PCT", // -768
|
||||||
"ML-KEM PCT",
|
"ML-KEM PCT", // -1024
|
||||||
"ML-KEM PCT",
|
|
||||||
"ML-KEM PCT",
|
|
||||||
"ML-KEM-768",
|
"ML-KEM-768",
|
||||||
"PBKDF2",
|
"PBKDF2",
|
||||||
"RSA sign and verify PCT",
|
"RSA sign and verify PCT",
|
||||||
|
|
@ -106,61 +104,77 @@ func TestAllCASTs(t *testing.T) {
|
||||||
|
|
||||||
// TestConditionals causes the conditional CASTs and PCTs to be invoked.
|
// TestConditionals causes the conditional CASTs and PCTs to be invoked.
|
||||||
func TestConditionals(t *testing.T) {
|
func TestConditionals(t *testing.T) {
|
||||||
mlkem.GenerateKey768()
|
// ML-KEM PCT
|
||||||
k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
|
kMLKEM, err := mlkem.GenerateKey768()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
// ML-KEM-768
|
||||||
|
kMLKEM.EncapsulationKey().Encapsulate()
|
||||||
}
|
}
|
||||||
ecdh.ECDH(ecdh.P256(), k, k.PublicKey())
|
// ECDH PCT
|
||||||
|
kDH, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
// KAS-ECC-SSC P-256
|
||||||
|
ecdh.ECDH(ecdh.P256(), kDH, kDH.PublicKey())
|
||||||
|
}
|
||||||
|
// ECDSA PCT
|
||||||
kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
|
kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
}
|
} else {
|
||||||
|
// ECDSA P-256 SHA2-512 sign and verify
|
||||||
ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
|
ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
|
||||||
|
}
|
||||||
|
// Ed25519 sign and verify PCT
|
||||||
k25519, err := ed25519.GenerateKey()
|
k25519, err := ed25519.GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
}
|
} else {
|
||||||
|
// Ed25519 sign and verify
|
||||||
ed25519.Sign(k25519, make([]byte, 32))
|
ed25519.Sign(k25519, make([]byte, 32))
|
||||||
rsa.VerifyPKCS1v15(&rsa.PublicKey{}, "", nil, nil)
|
}
|
||||||
// Parse an RSA key to hit the PCT rather than generating one (which is slow).
|
// RSA sign and verify PCT
|
||||||
block, _ := pem.Decode([]byte(strings.ReplaceAll(
|
kRSA, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
`-----BEGIN RSA TESTING KEY-----
|
if err != nil {
|
||||||
MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso
|
t.Error(err)
|
||||||
tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE
|
} else {
|
||||||
89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU
|
// RSASSA-PKCS-v1.5 2048-bit sign and verify
|
||||||
l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s
|
rsa.SignPKCS1v15(kRSA, crypto.SHA256.String(), make([]byte, 32))
|
||||||
B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59
|
|
||||||
3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+
|
|
||||||
dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI
|
|
||||||
FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J
|
|
||||||
aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2
|
|
||||||
BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx
|
|
||||||
IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/
|
|
||||||
fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u
|
|
||||||
pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT
|
|
||||||
Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl
|
|
||||||
u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD
|
|
||||||
fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X
|
|
||||||
Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE
|
|
||||||
k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo
|
|
||||||
qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS
|
|
||||||
CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ
|
|
||||||
XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw
|
|
||||||
AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r
|
|
||||||
UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
|
|
||||||
2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5
|
|
||||||
7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3
|
|
||||||
-----END RSA TESTING KEY-----`, "TESTING KEY", "PRIVATE KEY")))
|
|
||||||
if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
t.Log("completed successfully")
|
t.Log("completed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCASTPasses(t *testing.T) {
|
||||||
|
moduleStatus(t)
|
||||||
|
testenv.MustHaveExec(t)
|
||||||
|
cryptotest.MustSupportFIPS140(t)
|
||||||
|
|
||||||
|
cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
|
||||||
|
cmd.Env = append(cmd.Env, "GODEBUG=fips140=debug")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
t.Logf("%s", out)
|
||||||
|
if err != nil || !strings.Contains(string(out), "completed successfully") {
|
||||||
|
t.Errorf("TestConditionals did not complete successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range allCASTs {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
if !strings.Contains(string(out), fmt.Sprintf("passed: %s\n", name)) {
|
||||||
|
t.Errorf("CAST/PCT %s success was not logged", name)
|
||||||
|
} else {
|
||||||
|
t.Logf("CAST/PCT succeeded: %s", name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCASTFailures(t *testing.T) {
|
func TestCASTFailures(t *testing.T) {
|
||||||
moduleStatus(t)
|
moduleStatus(t)
|
||||||
testenv.MustHaveExec(t)
|
testenv.MustHaveExec(t)
|
||||||
|
cryptotest.MustSupportFIPS140(t)
|
||||||
|
|
||||||
for _, name := range allCASTs {
|
for _, name := range allCASTs {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
@ -169,7 +183,6 @@ func TestCASTFailures(t *testing.T) {
|
||||||
if !testing.Verbose() {
|
if !testing.Verbose() {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
}
|
}
|
||||||
t.Logf("CAST/PCT succeeded: %s", name)
|
|
||||||
t.Logf("Testing CAST/PCT failure...")
|
t.Logf("Testing CAST/PCT failure...")
|
||||||
cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
|
cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name))
|
||||||
|
|
@ -180,6 +193,8 @@ func TestCASTFailures(t *testing.T) {
|
||||||
}
|
}
|
||||||
if strings.Contains(string(out), "completed successfully") {
|
if strings.Contains(string(out), "completed successfully") {
|
||||||
t.Errorf("CAST/PCT %s failure did not stop the program", name)
|
t.Errorf("CAST/PCT %s failure did not stop the program", name)
|
||||||
|
} else if !strings.Contains(string(out), "self-test failed: "+name) {
|
||||||
|
t.Errorf("CAST/PCT %s failure did not log the expected message", name)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name)
|
t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
264
src/crypto/internal/fips140test/entropy_test.go
Normal file
264
src/crypto/internal/fips140test/entropy_test.go
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build !fips140v1.0
|
||||||
|
|
||||||
|
package fipstest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/internal/cryptotest"
|
||||||
|
"crypto/internal/fips140/drbg"
|
||||||
|
"crypto/internal/fips140/entropy"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"internal/testenv"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagEntropySamples = flag.String("entropy-samples", "", "store entropy samples with the provided `suffix`")
|
||||||
|
var flagNISTSP80090B = flag.Bool("nist-sp800-90b", false, "run NIST SP 800-90B tests (requires docker)")
|
||||||
|
|
||||||
|
func TestEntropySamples(t *testing.T) {
|
||||||
|
cryptotest.MustSupportFIPS140(t)
|
||||||
|
|
||||||
|
var seqSamples [1_000_000]uint8
|
||||||
|
samplesOrTryAgain(t, seqSamples[:])
|
||||||
|
seqSamplesName := fmt.Sprintf("entropy_samples_sequential_%s_%s_%s_%s_%s.bin", entropy.Version(),
|
||||||
|
runtime.GOOS, runtime.GOARCH, *flagEntropySamples, time.Now().Format("20060102T150405Z"))
|
||||||
|
if *flagEntropySamples != "" {
|
||||||
|
if err := os.WriteFile(seqSamplesName, seqSamples[:], 0644); err != nil {
|
||||||
|
t.Fatalf("failed to write samples to %q: %v", seqSamplesName, err)
|
||||||
|
}
|
||||||
|
t.Logf("wrote %s", seqSamplesName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var restartSamples [1000][1000]uint8
|
||||||
|
for i := range restartSamples {
|
||||||
|
var samples [1024]uint8
|
||||||
|
samplesOrTryAgain(t, samples[:])
|
||||||
|
copy(restartSamples[i][:], samples[:])
|
||||||
|
}
|
||||||
|
restartSamplesName := fmt.Sprintf("entropy_samples_restart_%s_%s_%s_%s_%s.bin", entropy.Version(),
|
||||||
|
runtime.GOOS, runtime.GOARCH, *flagEntropySamples, time.Now().Format("20060102T150405Z"))
|
||||||
|
if *flagEntropySamples != "" {
|
||||||
|
f, err := os.Create(restartSamplesName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create %q: %v", restartSamplesName, err)
|
||||||
|
}
|
||||||
|
for i := range restartSamples {
|
||||||
|
if _, err := f.Write(restartSamples[i][:]); err != nil {
|
||||||
|
t.Fatalf("failed to write samples to %q: %v", restartSamplesName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
t.Fatalf("failed to close %q: %v", restartSamplesName, err)
|
||||||
|
}
|
||||||
|
t.Logf("wrote %s", restartSamplesName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagNISTSP80090B {
|
||||||
|
if *flagEntropySamples == "" {
|
||||||
|
t.Fatalf("-nist-sp800-90b requires -entropy-samples to be set too")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the nist-sp800-90b docker image is already present,
|
||||||
|
// and build it otherwise.
|
||||||
|
if err := testenv.Command(t,
|
||||||
|
"docker", "image", "inspect", "nist-sp800-90b",
|
||||||
|
).Run(); err != nil {
|
||||||
|
t.Logf("building nist-sp800-90b docker image")
|
||||||
|
dockerfile := filepath.Join(t.TempDir(), "Dockerfile.SP800-90B_EntropyAssessment")
|
||||||
|
if err := os.WriteFile(dockerfile, []byte(NISTSP80090BDockerfile), 0644); err != nil {
|
||||||
|
t.Fatalf("failed to write Dockerfile: %v", err)
|
||||||
|
}
|
||||||
|
out, err := testenv.Command(t,
|
||||||
|
"docker", "build", "-t", "nist-sp800-90b", "-f", dockerfile, "/var/empty",
|
||||||
|
).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to build nist-sp800-90b docker image: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get current working directory: %v", err)
|
||||||
|
}
|
||||||
|
t.Logf("running ea_non_iid analysis")
|
||||||
|
out, err := testenv.Command(t,
|
||||||
|
"docker", "run", "--rm", "-v", fmt.Sprintf("%s:%s", pwd, pwd), "-w", pwd,
|
||||||
|
"nist-sp800-90b", "ea_non_iid", seqSamplesName, "8",
|
||||||
|
).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ea_non_iid failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
t.Logf("\n%s", out)
|
||||||
|
|
||||||
|
H_I := string(out)
|
||||||
|
H_I = strings.TrimSpace(H_I[strings.LastIndexByte(H_I, ' ')+1:])
|
||||||
|
t.Logf("running ea_restart analysis with H_I = %s", H_I)
|
||||||
|
out, err = testenv.Command(t,
|
||||||
|
"docker", "run", "--rm", "-v", fmt.Sprintf("%s:%s", pwd, pwd), "-w", pwd,
|
||||||
|
"nist-sp800-90b", "ea_restart", restartSamplesName, "8", H_I,
|
||||||
|
).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ea_restart failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
t.Logf("\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var NISTSP80090BDockerfile = `
|
||||||
|
FROM ubuntu:24.04
|
||||||
|
RUN apt-get update && apt-get install -y build-essential git \
|
||||||
|
libbz2-dev libdivsufsort-dev libjsoncpp-dev libgmp-dev libmpfr-dev libssl-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN git clone --depth 1 https://github.com/usnistgov/SP800-90B_EntropyAssessment.git
|
||||||
|
RUN cd SP800-90B_EntropyAssessment && git checkout 8924f158c97e7b805e0f95247403ad4c44b9cd6f
|
||||||
|
WORKDIR ./SP800-90B_EntropyAssessment/cpp/
|
||||||
|
RUN make all
|
||||||
|
RUN cd selftest && ./selftest
|
||||||
|
RUN cp ea_non_iid ea_restart /usr/local/bin/
|
||||||
|
`
|
||||||
|
|
||||||
|
var memory entropy.ScratchBuffer
|
||||||
|
|
||||||
|
// samplesOrTryAgain calls entropy.Samples up to 10 times until it succeeds.
|
||||||
|
// Samples has a non-negligible chance of failing the health tests, as required
|
||||||
|
// by SP 800-90B.
|
||||||
|
func samplesOrTryAgain(t *testing.T, samples []uint8) {
|
||||||
|
t.Helper()
|
||||||
|
for range 10 {
|
||||||
|
if err := entropy.Samples(samples, &memory); err != nil {
|
||||||
|
t.Logf("entropy.Samples() failed: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatal("entropy.Samples() failed 10 times in a row")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntropySHA384(t *testing.T) {
|
||||||
|
var input [1024]uint8
|
||||||
|
for i := range input {
|
||||||
|
input[i] = uint8(i)
|
||||||
|
}
|
||||||
|
want := sha512.Sum384(input[:])
|
||||||
|
got := entropy.SHA384(&input)
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("SHA384() = %x, want %x", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntropyRepetitionCountTest(t *testing.T) {
|
||||||
|
good := bytes.Repeat(append(bytes.Repeat([]uint8{42}, 40), 1), 100)
|
||||||
|
if err := entropy.RepetitionCountTest(good); err != nil {
|
||||||
|
t.Errorf("RepetitionCountTest(good) = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bad := bytes.Repeat([]uint8{0}, 40)
|
||||||
|
bad = append(bad, bytes.Repeat([]uint8{1}, 40)...)
|
||||||
|
bad = append(bad, bytes.Repeat([]uint8{42}, 41)...)
|
||||||
|
bad = append(bad, bytes.Repeat([]uint8{2}, 40)...)
|
||||||
|
if err := entropy.RepetitionCountTest(bad); err == nil {
|
||||||
|
t.Error("RepetitionCountTest(bad) = nil, want error")
|
||||||
|
}
|
||||||
|
|
||||||
|
bad = bytes.Repeat([]uint8{42}, 41)
|
||||||
|
if err := entropy.RepetitionCountTest(bad); err == nil {
|
||||||
|
t.Error("RepetitionCountTest(bad) = nil, want error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntropyAdaptiveProportionTest(t *testing.T) {
|
||||||
|
good := bytes.Repeat([]uint8{0}, 409)
|
||||||
|
good = append(good, bytes.Repeat([]uint8{1}, 512-409)...)
|
||||||
|
good = append(good, bytes.Repeat([]uint8{0}, 409)...)
|
||||||
|
if err := entropy.AdaptiveProportionTest(good); err != nil {
|
||||||
|
t.Errorf("AdaptiveProportionTest(good) = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// These fall out of the window.
|
||||||
|
bad := bytes.Repeat([]uint8{1}, 100)
|
||||||
|
bad = append(bad, bytes.Repeat([]uint8{1, 2, 3, 4, 5, 6}, 100)...)
|
||||||
|
// These are in the window.
|
||||||
|
bad = append(bad, bytes.Repeat([]uint8{42}, 410)...)
|
||||||
|
if err := entropy.AdaptiveProportionTest(bad[:len(bad)-1]); err != nil {
|
||||||
|
t.Errorf("AdaptiveProportionTest(bad[:len(bad)-1]) = %v, want nil", err)
|
||||||
|
}
|
||||||
|
if err := entropy.AdaptiveProportionTest(bad); err == nil {
|
||||||
|
t.Error("AdaptiveProportionTest(bad) = nil, want error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntropyUnchanged(t *testing.T) {
|
||||||
|
testenv.MustHaveSource(t)
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
root := os.DirFS("../fips140/entropy")
|
||||||
|
if err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data, err := fs.ReadFile(root, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Logf("Hashing %s (%d bytes)", path, len(data))
|
||||||
|
fmt.Fprintf(h, "%s %d\n", path, len(data))
|
||||||
|
h.Write(data)
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("WalkDir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The crypto/internal/fips140/entropy package is certified as a FIPS 140-3
|
||||||
|
// entropy source through the Entropy Source Validation program,
|
||||||
|
// independently of the FIPS 140-3 module. It must not change even across
|
||||||
|
// FIPS 140-3 module versions, in order to reuse the ESV certificate.
|
||||||
|
exp := "35976eb8a11678c79777da07aaab5511d4325701f837777df205f6e7b20c6821"
|
||||||
|
if got := hex.EncodeToString(h.Sum(nil)); got != exp {
|
||||||
|
t.Errorf("hash of crypto/internal/fips140/entropy = %s, want %s", got, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntropyRace(t *testing.T) {
|
||||||
|
// Check that concurrent calls to Seed don't trigger the race detector.
|
||||||
|
for range 2 {
|
||||||
|
go func() {
|
||||||
|
_, _ = entropy.Seed(&memory)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// Same, with the higher-level DRBG. More concurrent calls to hit the Pool.
|
||||||
|
for range 16 {
|
||||||
|
go func() {
|
||||||
|
var b [64]byte
|
||||||
|
drbg.Read(b[:])
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sink byte
|
||||||
|
|
||||||
|
func BenchmarkEntropySeed(b *testing.B) {
|
||||||
|
for b.Loop() {
|
||||||
|
seed, err := entropy.Seed(&memory)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("entropy.Seed() failed: %v", err)
|
||||||
|
}
|
||||||
|
sink ^= seed[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -74,11 +74,9 @@ func TestVersion(t *testing.T) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
exp := setting.Value
|
exp := setting.Value
|
||||||
if exp == "v1.0.0" {
|
// Remove the -hash suffix, if any.
|
||||||
// Unfortunately we enshrined the version of the first module as
|
// The version from fips140.Version omits it.
|
||||||
// v1.0 before deciding to go for full versions.
|
exp, _, _ = strings.Cut(exp, "-")
|
||||||
exp = "v1.0"
|
|
||||||
}
|
|
||||||
if v := fips140.Version(); v != exp {
|
if v := fips140.Version(); v != exp {
|
||||||
t.Errorf("Version is %q, expected %q", v, exp)
|
t.Errorf("Version is %q, expected %q", v, exp)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1246,7 +1246,7 @@ func TestModifiedPrivateKey(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("D+2", func(t *testing.T) {
|
t.Run("D+2", func(t *testing.T) {
|
||||||
if fips140.Version() == "v1.0" {
|
if fips140.Version() == "v1.0.0" {
|
||||||
t.Skip("This was fixed after v1.0.0")
|
t.Skip("This was fixed after v1.0.0")
|
||||||
}
|
}
|
||||||
testModifiedPrivateKey(t, func(k *PrivateKey) {
|
testModifiedPrivateKey(t, func(k *PrivateKey) {
|
||||||
|
|
|
||||||
|
|
@ -568,16 +568,6 @@ func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8,
|
||||||
return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
|
return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([]byte, error) {
|
|
||||||
builder := cryptobyte.NewBuilder(nil)
|
|
||||||
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
|
||||||
for _, c := range configs {
|
|
||||||
builder.AddBytes(c.Config)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return builder.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
|
func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
|
||||||
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
|
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -357,7 +357,7 @@ func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, erro
|
||||||
if http11fallback {
|
if http11fallback {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
|
return "", fmt.Errorf("tls: client requested unsupported application protocols (%q)", clientProtos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ type echServerContext struct {
|
||||||
configID uint8
|
configID uint8
|
||||||
ciphersuite echCipher
|
ciphersuite echCipher
|
||||||
transcript hash.Hash
|
transcript hash.Hash
|
||||||
// inner indicates that the initial client_hello we recieved contained an
|
// inner indicates that the initial client_hello we received contained an
|
||||||
// encrypted_client_hello extension that indicated it was an "inner" hello.
|
// encrypted_client_hello extension that indicated it was an "inner" hello.
|
||||||
// We don't do any additional processing of the hello in this case, so all
|
// We don't do any additional processing of the hello in this case, so all
|
||||||
// fields above are unset.
|
// fields above are unset.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"internal/saferio"
|
"internal/saferio"
|
||||||
"internal/zstd"
|
"internal/zstd"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
@ -830,17 +831,9 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_X86_64_64:
|
case R_X86_64_64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_X86_64_32:
|
case R_X86_64_32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -872,12 +865,7 @@ func (f *File) applyRelocations386(dst []byte, rels []byte) error {
|
||||||
sym := &symbols[symNo-1]
|
sym := &symbols[symNo-1]
|
||||||
|
|
||||||
if t == R_386_32 {
|
if t == R_386_32 {
|
||||||
if rel.Off+4 >= uint32(len(dst)) {
|
putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
|
|
||||||
val += uint32(sym.Value)
|
|
||||||
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -910,12 +898,7 @@ func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_ARM_ABS32:
|
case R_ARM_ABS32:
|
||||||
if rel.Off+4 >= uint32(len(dst)) {
|
putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
|
|
||||||
val += uint32(sym.Value)
|
|
||||||
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -955,17 +938,9 @@ func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_AARCH64_ABS64:
|
case R_AARCH64_ABS64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_AARCH64_ABS32:
|
case R_AARCH64_ABS32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1001,11 +976,7 @@ func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_PPC_ADDR32:
|
case R_PPC_ADDR32:
|
||||||
if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, uint64(rela.Off), 4, sym.Value, 0, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1041,17 +1012,9 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_PPC64_ADDR64:
|
case R_PPC64_ADDR64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_PPC64_ADDR32:
|
case R_PPC64_ADDR32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1084,12 +1047,7 @@ func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_MIPS_32:
|
case R_MIPS_32:
|
||||||
if rel.Off+4 >= uint32(len(dst)) {
|
putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
|
|
||||||
val += uint32(sym.Value)
|
|
||||||
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1132,17 +1090,9 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_MIPS_64:
|
case R_MIPS_64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_MIPS_32:
|
case R_MIPS_32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1180,17 +1130,9 @@ func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_LARCH_64:
|
case R_LARCH_64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_LARCH_32:
|
case R_LARCH_32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1226,17 +1168,9 @@ func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_RISCV_64:
|
case R_RISCV_64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_RISCV_32:
|
case R_RISCV_32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1272,17 +1206,9 @@ func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_390_64:
|
case R_390_64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_390_32:
|
case R_390_32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1318,17 +1244,10 @@ func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case R_SPARC_64, R_SPARC_UA64:
|
case R_SPARC_64, R_SPARC_UA64:
|
||||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val64 := sym.Value + uint64(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
|
|
||||||
case R_SPARC_32, R_SPARC_UA32:
|
case R_SPARC_32, R_SPARC_UA32:
|
||||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
val32 := uint32(sym.Value) + uint32(rela.Addend)
|
|
||||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1903,3 +1822,38 @@ type nobitsSectionReader struct{}
|
||||||
func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) {
|
func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
return 0, errors.New("unexpected read from SHT_NOBITS section")
|
return 0, errors.New("unexpected read from SHT_NOBITS section")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// putUint writes a relocation to slice
|
||||||
|
// at offset start of length length (4 or 8 bytes),
|
||||||
|
// adding sym+addend to the existing value if readUint is true,
|
||||||
|
// or just writing sym+addend if readUint is false.
|
||||||
|
// If the write would extend beyond the end of slice, putUint does nothing.
|
||||||
|
// If the addend is negative, putUint does nothing.
|
||||||
|
// If the addition would overflow, putUint does nothing.
|
||||||
|
func putUint(byteOrder binary.ByteOrder, slice []byte, start, length, sym uint64, addend int64, readUint bool) {
|
||||||
|
if start+length > uint64(len(slice)) || math.MaxUint64-start < length {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if addend < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s := slice[start : start+length]
|
||||||
|
|
||||||
|
switch length {
|
||||||
|
case 4:
|
||||||
|
ae := uint32(addend)
|
||||||
|
if readUint {
|
||||||
|
ae += byteOrder.Uint32(s)
|
||||||
|
}
|
||||||
|
byteOrder.PutUint32(s, uint32(sym)+ae)
|
||||||
|
case 8:
|
||||||
|
ae := uint64(addend)
|
||||||
|
if readUint {
|
||||||
|
ae += byteOrder.Uint64(s)
|
||||||
|
}
|
||||||
|
byteOrder.PutUint64(s, sym+ae)
|
||||||
|
default:
|
||||||
|
panic("can't happen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -557,3 +557,67 @@ func TestTokenTruncation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecoderInputOffset(t *testing.T) {
|
||||||
|
const input = ` [
|
||||||
|
[ ] , [ "one" ] , [ "one" , "two" ] ,
|
||||||
|
{ } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
|
||||||
|
] `
|
||||||
|
wantOffsets := []int64{
|
||||||
|
0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
|
||||||
|
38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
|
||||||
|
76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
|
||||||
|
117, 117,
|
||||||
|
}
|
||||||
|
wantMores := []bool{
|
||||||
|
true, true, false, true, true, false, true, true, true, false,
|
||||||
|
true, false, true, true, true, false, true, true, true, true,
|
||||||
|
true, false, false, false, false,
|
||||||
|
}
|
||||||
|
|
||||||
|
d := NewDecoder(strings.NewReader(input))
|
||||||
|
checkOffset := func() {
|
||||||
|
t.Helper()
|
||||||
|
got := d.InputOffset()
|
||||||
|
if len(wantOffsets) == 0 {
|
||||||
|
t.Fatalf("InputOffset = %d, want nil", got)
|
||||||
|
}
|
||||||
|
want := wantOffsets[0]
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("InputOffset = %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
wantOffsets = wantOffsets[1:]
|
||||||
|
}
|
||||||
|
checkMore := func() {
|
||||||
|
t.Helper()
|
||||||
|
got := d.More()
|
||||||
|
if len(wantMores) == 0 {
|
||||||
|
t.Fatalf("More = %v, want nil", got)
|
||||||
|
}
|
||||||
|
want := wantMores[0]
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("More = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
wantMores = wantMores[1:]
|
||||||
|
}
|
||||||
|
checkOffset()
|
||||||
|
checkMore()
|
||||||
|
checkOffset()
|
||||||
|
for {
|
||||||
|
if _, err := d.Token(); err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("Token error: %v", err)
|
||||||
|
}
|
||||||
|
checkOffset()
|
||||||
|
checkMore()
|
||||||
|
checkOffset()
|
||||||
|
}
|
||||||
|
checkOffset()
|
||||||
|
checkMore()
|
||||||
|
checkOffset()
|
||||||
|
|
||||||
|
if len(wantOffsets)+len(wantMores) > 0 {
|
||||||
|
t.Fatal("unconsumed testdata")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ type Decoder struct {
|
||||||
dec *jsontext.Decoder
|
dec *jsontext.Decoder
|
||||||
opts jsonv2.Options
|
opts jsonv2.Options
|
||||||
err error
|
err error
|
||||||
|
|
||||||
|
// hadPeeked reports whether [Decoder.More] was called.
|
||||||
|
// It is reset by [Decoder.Decode] and [Decoder.Token].
|
||||||
|
hadPeeked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDecoder returns a new decoder that reads from r.
|
// NewDecoder returns a new decoder that reads from r.
|
||||||
|
|
@ -76,6 +80,7 @@ func (dec *Decoder) Decode(v any) error {
|
||||||
}
|
}
|
||||||
return dec.err
|
return dec.err
|
||||||
}
|
}
|
||||||
|
dec.hadPeeked = false
|
||||||
return jsonv2.Unmarshal(b, v, dec.opts)
|
return jsonv2.Unmarshal(b, v, dec.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,6 +211,7 @@ func (dec *Decoder) Token() (Token, error) {
|
||||||
}
|
}
|
||||||
return nil, transformSyntacticError(err)
|
return nil, transformSyntacticError(err)
|
||||||
}
|
}
|
||||||
|
dec.hadPeeked = false
|
||||||
switch k := tok.Kind(); k {
|
switch k := tok.Kind(); k {
|
||||||
case 'n':
|
case 'n':
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -230,6 +236,7 @@ func (dec *Decoder) Token() (Token, error) {
|
||||||
// More reports whether there is another element in the
|
// More reports whether there is another element in the
|
||||||
// current array or object being parsed.
|
// current array or object being parsed.
|
||||||
func (dec *Decoder) More() bool {
|
func (dec *Decoder) More() bool {
|
||||||
|
dec.hadPeeked = true
|
||||||
k := dec.dec.PeekKind()
|
k := dec.dec.PeekKind()
|
||||||
return k > 0 && k != ']' && k != '}'
|
return k > 0 && k != ']' && k != '}'
|
||||||
}
|
}
|
||||||
|
|
@ -238,5 +245,18 @@ func (dec *Decoder) More() bool {
|
||||||
// The offset gives the location of the end of the most recently returned token
|
// The offset gives the location of the end of the most recently returned token
|
||||||
// and the beginning of the next token.
|
// and the beginning of the next token.
|
||||||
func (dec *Decoder) InputOffset() int64 {
|
func (dec *Decoder) InputOffset() int64 {
|
||||||
return dec.dec.InputOffset()
|
offset := dec.dec.InputOffset()
|
||||||
|
if dec.hadPeeked {
|
||||||
|
// Historically, InputOffset reported the location of
|
||||||
|
// the end of the most recently returned token
|
||||||
|
// unless [Decoder.More] is called, in which case, it reported
|
||||||
|
// the beginning of the next token.
|
||||||
|
unreadBuffer := dec.dec.UnreadBuffer()
|
||||||
|
trailingTokens := bytes.TrimLeft(unreadBuffer, " \n\r\t")
|
||||||
|
if len(trailingTokens) > 0 {
|
||||||
|
leadingWhitespace := len(unreadBuffer) - len(trailingTokens)
|
||||||
|
offset += int64(leadingWhitespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -537,3 +537,67 @@ func TestTokenTruncation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecoderInputOffset(t *testing.T) {
|
||||||
|
const input = ` [
|
||||||
|
[ ] , [ "one" ] , [ "one" , "two" ] ,
|
||||||
|
{ } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
|
||||||
|
] `
|
||||||
|
wantOffsets := []int64{
|
||||||
|
0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
|
||||||
|
38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
|
||||||
|
76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
|
||||||
|
117, 117,
|
||||||
|
}
|
||||||
|
wantMores := []bool{
|
||||||
|
true, true, false, true, true, false, true, true, true, false,
|
||||||
|
true, false, true, true, true, false, true, true, true, true,
|
||||||
|
true, false, false, false, false,
|
||||||
|
}
|
||||||
|
|
||||||
|
d := NewDecoder(strings.NewReader(input))
|
||||||
|
checkOffset := func() {
|
||||||
|
t.Helper()
|
||||||
|
got := d.InputOffset()
|
||||||
|
if len(wantOffsets) == 0 {
|
||||||
|
t.Fatalf("InputOffset = %d, want nil", got)
|
||||||
|
}
|
||||||
|
want := wantOffsets[0]
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("InputOffset = %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
wantOffsets = wantOffsets[1:]
|
||||||
|
}
|
||||||
|
checkMore := func() {
|
||||||
|
t.Helper()
|
||||||
|
got := d.More()
|
||||||
|
if len(wantMores) == 0 {
|
||||||
|
t.Fatalf("More = %v, want nil", got)
|
||||||
|
}
|
||||||
|
want := wantMores[0]
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("More = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
wantMores = wantMores[1:]
|
||||||
|
}
|
||||||
|
checkOffset()
|
||||||
|
checkMore()
|
||||||
|
checkOffset()
|
||||||
|
for {
|
||||||
|
if _, err := d.Token(); err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("Token error: %v", err)
|
||||||
|
}
|
||||||
|
checkOffset()
|
||||||
|
checkMore()
|
||||||
|
checkOffset()
|
||||||
|
}
|
||||||
|
checkOffset()
|
||||||
|
checkMore()
|
||||||
|
checkOffset()
|
||||||
|
|
||||||
|
if len(wantOffsets)+len(wantMores) > 0 {
|
||||||
|
t.Fatal("unconsumed testdata")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -416,12 +416,6 @@ func (p *printer) popPrefix() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
marshalerType = reflect.TypeFor[Marshaler]()
|
|
||||||
marshalerAttrType = reflect.TypeFor[MarshalerAttr]()
|
|
||||||
textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
|
|
||||||
)
|
|
||||||
|
|
||||||
// marshalValue writes one or more XML elements representing val.
|
// marshalValue writes one or more XML elements representing val.
|
||||||
// If val was obtained from a struct field, finfo must have its details.
|
// If val was obtained from a struct field, finfo must have its details.
|
||||||
func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
|
func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
|
||||||
|
|
@ -450,24 +444,32 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
||||||
typ := val.Type()
|
typ := val.Type()
|
||||||
|
|
||||||
// Check for marshaler.
|
// Check for marshaler.
|
||||||
if val.CanInterface() && typ.Implements(marshalerType) {
|
if val.CanInterface() {
|
||||||
return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate))
|
if marshaler, ok := reflect.TypeAssert[Marshaler](val); ok {
|
||||||
|
return p.marshalInterface(marshaler, defaultStart(typ, finfo, startTemplate))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(marshalerType) {
|
if pv.CanInterface() {
|
||||||
return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate))
|
if marshaler, ok := reflect.TypeAssert[Marshaler](pv); ok {
|
||||||
|
return p.marshalInterface(marshaler, defaultStart(pv.Type(), finfo, startTemplate))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for text marshaler.
|
// Check for text marshaler.
|
||||||
if val.CanInterface() && typ.Implements(textMarshalerType) {
|
if val.CanInterface() {
|
||||||
return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate))
|
if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](val); ok {
|
||||||
|
return p.marshalTextInterface(textMarshaler, defaultStart(typ, finfo, startTemplate))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
|
if pv.CanInterface() {
|
||||||
return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate))
|
if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](pv); ok {
|
||||||
|
return p.marshalTextInterface(textMarshaler, defaultStart(pv.Type(), finfo, startTemplate))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -503,7 +505,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
||||||
start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
|
start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
|
||||||
} else {
|
} else {
|
||||||
fv := xmlname.value(val, dontInitNilPointers)
|
fv := xmlname.value(val, dontInitNilPointers)
|
||||||
if v, ok := fv.Interface().(Name); ok && v.Local != "" {
|
if v, ok := reflect.TypeAssert[Name](fv); ok && v.Local != "" {
|
||||||
start.Name = v
|
start.Name = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -580,21 +582,9 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
||||||
|
|
||||||
// marshalAttr marshals an attribute with the given name and value, adding to start.Attr.
|
// marshalAttr marshals an attribute with the given name and value, adding to start.Attr.
|
||||||
func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) error {
|
func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) error {
|
||||||
if val.CanInterface() && val.Type().Implements(marshalerAttrType) {
|
if val.CanInterface() {
|
||||||
attr, err := val.Interface().(MarshalerAttr).MarshalXMLAttr(name)
|
if marshaler, ok := reflect.TypeAssert[MarshalerAttr](val); ok {
|
||||||
if err != nil {
|
attr, err := marshaler.MarshalXMLAttr(name)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if attr.Name.Local != "" {
|
|
||||||
start.Attr = append(start.Attr, attr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if val.CanAddr() {
|
|
||||||
pv := val.Addr()
|
|
||||||
if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
|
|
||||||
attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -605,19 +595,38 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.CanInterface() && val.Type().Implements(textMarshalerType) {
|
if val.CanAddr() {
|
||||||
text, err := val.Interface().(encoding.TextMarshaler).MarshalText()
|
pv := val.Addr()
|
||||||
|
if pv.CanInterface() {
|
||||||
|
if marshaler, ok := reflect.TypeAssert[MarshalerAttr](pv); ok {
|
||||||
|
attr, err := marshaler.MarshalXMLAttr(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if attr.Name.Local != "" {
|
||||||
|
start.Attr = append(start.Attr, attr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.CanInterface() {
|
||||||
|
if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](val); ok {
|
||||||
|
text, err := textMarshaler.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
start.Attr = append(start.Attr, Attr{name, string(text)})
|
start.Attr = append(start.Attr, Attr{name, string(text)})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
|
if pv.CanInterface() {
|
||||||
text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
|
if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](pv); ok {
|
||||||
|
text, err := textMarshaler.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -625,6 +634,7 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dereference or skip nil pointer, interface values.
|
// Dereference or skip nil pointer, interface values.
|
||||||
switch val.Kind() {
|
switch val.Kind() {
|
||||||
|
|
@ -647,7 +657,8 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.Type() == attrType {
|
if val.Type() == attrType {
|
||||||
start.Attr = append(start.Attr, val.Interface().(Attr))
|
attr, _ := reflect.TypeAssert[Attr](val)
|
||||||
|
start.Attr = append(start.Attr, attr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -854,8 +865,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||||
if err := s.trim(finfo.parents); err != nil {
|
if err := s.trim(finfo.parents); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if vf.CanInterface() && vf.Type().Implements(textMarshalerType) {
|
if vf.CanInterface() {
|
||||||
data, err := vf.Interface().(encoding.TextMarshaler).MarshalText()
|
if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](vf); ok {
|
||||||
|
data, err := textMarshaler.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -864,10 +876,12 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if vf.CanAddr() {
|
if vf.CanAddr() {
|
||||||
pv := vf.Addr()
|
pv := vf.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
|
if pv.CanInterface() {
|
||||||
data, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
|
if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](pv); ok {
|
||||||
|
data, err := textMarshaler.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -877,6 +891,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var scratch [64]byte
|
var scratch [64]byte
|
||||||
vf = indirect(vf)
|
vf = indirect(vf)
|
||||||
|
|
@ -902,7 +917,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if elem, ok := vf.Interface().([]byte); ok {
|
if elem, ok := reflect.TypeAssert[[]byte](vf); ok {
|
||||||
if err := emit(p, elem); err != nil {
|
if err := emit(p, elem); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -255,28 +255,36 @@ func (d *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
|
||||||
}
|
}
|
||||||
val = val.Elem()
|
val = val.Elem()
|
||||||
}
|
}
|
||||||
if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) {
|
if val.CanInterface() {
|
||||||
// This is an unmarshaler with a non-pointer receiver,
|
// This is an unmarshaler with a non-pointer receiver,
|
||||||
// so it's likely to be incorrect, but we do what we're told.
|
// so it's likely to be incorrect, but we do what we're told.
|
||||||
return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
|
if unmarshaler, ok := reflect.TypeAssert[UnmarshalerAttr](val); ok {
|
||||||
|
return unmarshaler.UnmarshalXMLAttr(attr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) {
|
if pv.CanInterface() {
|
||||||
return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
|
if unmarshaler, ok := reflect.TypeAssert[UnmarshalerAttr](pv); ok {
|
||||||
|
return unmarshaler.UnmarshalXMLAttr(attr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not an UnmarshalerAttr; try encoding.TextUnmarshaler.
|
// Not an UnmarshalerAttr; try encoding.TextUnmarshaler.
|
||||||
if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
|
if val.CanInterface() {
|
||||||
// This is an unmarshaler with a non-pointer receiver,
|
// This is an unmarshaler with a non-pointer receiver,
|
||||||
// so it's likely to be incorrect, but we do what we're told.
|
// so it's likely to be incorrect, but we do what we're told.
|
||||||
return val.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
|
if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](val); ok {
|
||||||
|
return textUnmarshaler.UnmarshalText([]byte(attr.Value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
|
if pv.CanInterface() {
|
||||||
return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
|
if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](pv); ok {
|
||||||
|
return textUnmarshaler.UnmarshalText([]byte(attr.Value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,12 +311,7 @@ func (d *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
|
||||||
return copyValue(val, []byte(attr.Value))
|
return copyValue(val, []byte(attr.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var attrType = reflect.TypeFor[Attr]()
|
||||||
attrType = reflect.TypeFor[Attr]()
|
|
||||||
unmarshalerType = reflect.TypeFor[Unmarshaler]()
|
|
||||||
unmarshalerAttrType = reflect.TypeFor[UnmarshalerAttr]()
|
|
||||||
textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]()
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxUnmarshalDepth = 10000
|
maxUnmarshalDepth = 10000
|
||||||
|
|
@ -352,27 +355,35 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) e
|
||||||
val = val.Elem()
|
val = val.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.CanInterface() && val.Type().Implements(unmarshalerType) {
|
if val.CanInterface() {
|
||||||
// This is an unmarshaler with a non-pointer receiver,
|
// This is an unmarshaler with a non-pointer receiver,
|
||||||
// so it's likely to be incorrect, but we do what we're told.
|
// so it's likely to be incorrect, but we do what we're told.
|
||||||
return d.unmarshalInterface(val.Interface().(Unmarshaler), start)
|
if unmarshaler, ok := reflect.TypeAssert[Unmarshaler](val); ok {
|
||||||
|
return d.unmarshalInterface(unmarshaler, start)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(unmarshalerType) {
|
if pv.CanInterface() {
|
||||||
return d.unmarshalInterface(pv.Interface().(Unmarshaler), start)
|
if unmarshaler, ok := reflect.TypeAssert[Unmarshaler](pv); ok {
|
||||||
|
return d.unmarshalInterface(unmarshaler, start)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
|
if val.CanInterface() {
|
||||||
return d.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler))
|
if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](val); ok {
|
||||||
|
return d.unmarshalTextInterface(textUnmarshaler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.CanAddr() {
|
if val.CanAddr() {
|
||||||
pv := val.Addr()
|
pv := val.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
|
if pv.CanInterface() {
|
||||||
return d.unmarshalTextInterface(pv.Interface().(encoding.TextUnmarshaler))
|
if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](pv); ok {
|
||||||
|
return d.unmarshalTextInterface(textUnmarshaler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,7 +464,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) e
|
||||||
return UnmarshalError(e)
|
return UnmarshalError(e)
|
||||||
}
|
}
|
||||||
fv := finfo.value(sv, initNilPointers)
|
fv := finfo.value(sv, initNilPointers)
|
||||||
if _, ok := fv.Interface().(Name); ok {
|
if _, ok := reflect.TypeAssert[Name](fv); ok {
|
||||||
fv.Set(reflect.ValueOf(start.Name))
|
fv.Set(reflect.ValueOf(start.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -578,22 +589,26 @@ Loop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveData.IsValid() && saveData.CanInterface() && saveData.Type().Implements(textUnmarshalerType) {
|
if saveData.IsValid() && saveData.CanInterface() {
|
||||||
if err := saveData.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
|
if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](saveData); ok {
|
||||||
|
if err := textUnmarshaler.UnmarshalText(data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
saveData = reflect.Value{}
|
saveData = reflect.Value{}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if saveData.IsValid() && saveData.CanAddr() {
|
if saveData.IsValid() && saveData.CanAddr() {
|
||||||
pv := saveData.Addr()
|
pv := saveData.Addr()
|
||||||
if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
|
if pv.CanInterface() {
|
||||||
if err := pv.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
|
if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](pv); ok {
|
||||||
|
if err := textUnmarshaler.UnmarshalText(data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
saveData = reflect.Value{}
|
saveData = reflect.Value{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := copyValue(saveData, data); err != nil {
|
if err := copyValue(saveData, data); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@
|
||||||
//
|
//
|
||||||
// because the former will succeed if err wraps [io/fs.ErrExist].
|
// because the former will succeed if err wraps [io/fs.ErrExist].
|
||||||
//
|
//
|
||||||
// [As] examines the tree of its first argument looking for an error that can be
|
// [AsType] examines the tree of its argument looking for an error whose
|
||||||
// assigned to its second argument, which must be a pointer. If it succeeds, it
|
// type matches its type argument. If it succeeds, it returns the
|
||||||
// performs the assignment and returns true. Otherwise, it returns false. The form
|
// corresponding value of that type and true. Otherwise, it returns the
|
||||||
|
// zero value of that type and false. The form
|
||||||
//
|
//
|
||||||
// var perr *fs.PathError
|
// if perr, ok := errors.AsType[*fs.PathError](err); ok {
|
||||||
// if errors.As(err, &perr) {
|
|
||||||
// fmt.Println(perr.Path)
|
// fmt.Println(perr.Path)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,18 @@ func ExampleAs() {
|
||||||
// Failed at path: non-existing
|
// Failed at path: non-existing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleAsType() {
|
||||||
|
if _, err := os.Open("non-existing"); err != nil {
|
||||||
|
if pathError, ok := errors.AsType[*fs.PathError](err); ok {
|
||||||
|
fmt.Println("Failed at path:", pathError.Path)
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Output:
|
||||||
|
// Failed at path: non-existing
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleUnwrap() {
|
func ExampleUnwrap() {
|
||||||
err1 := errors.New("error1")
|
err1 := errors.New("error1")
|
||||||
err2 := fmt.Errorf("error2: [%w]", err1)
|
err2 := fmt.Errorf("error2: [%w]", err1)
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,10 @@ func is(err, target error, targetComparable bool) bool {
|
||||||
// As finds the first error in err's tree that matches target, and if one is found, sets
|
// As finds the first error in err's tree that matches target, and if one is found, sets
|
||||||
// target to that error value and returns true. Otherwise, it returns false.
|
// target to that error value and returns true. Otherwise, it returns false.
|
||||||
//
|
//
|
||||||
|
// For most uses, prefer [AsType]. As is equivalent to [AsType] but sets its target
|
||||||
|
// argument rather than returning the matching error and doesn't require its target
|
||||||
|
// argument to implement error.
|
||||||
|
//
|
||||||
// The tree consists of err itself, followed by the errors obtained by repeatedly
|
// The tree consists of err itself, followed by the errors obtained by repeatedly
|
||||||
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
|
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
|
||||||
// errors, As examines err followed by a depth-first traversal of its children.
|
// errors, As examines err followed by a depth-first traversal of its children.
|
||||||
|
|
@ -145,3 +149,60 @@ func as(err error, target any, targetVal reflectlite.Value, targetType reflectli
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
|
var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
|
||||||
|
|
||||||
|
// AsType finds the first error in err's tree that matches the type E, and
|
||||||
|
// if one is found, returns that error value and true. Otherwise, it
|
||||||
|
// returns the zero value of E and false.
|
||||||
|
//
|
||||||
|
// The tree consists of err itself, followed by the errors obtained by
|
||||||
|
// repeatedly calling its Unwrap() error or Unwrap() []error method. When
|
||||||
|
// err wraps multiple errors, AsType examines err followed by a
|
||||||
|
// depth-first traversal of its children.
|
||||||
|
//
|
||||||
|
// An error err matches the type E if the type assertion err.(E) holds,
|
||||||
|
// or if the error has a method As(any) bool such that err.As(target)
|
||||||
|
// returns true when target is a non-nil *E. In the latter case, the As
|
||||||
|
// method is responsible for setting target.
|
||||||
|
func AsType[E error](err error) (E, bool) {
|
||||||
|
if err == nil {
|
||||||
|
var zero E
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
var pe *E // lazily initialized
|
||||||
|
return asType(err, &pe)
|
||||||
|
}
|
||||||
|
|
||||||
|
func asType[E error](err error, ppe **E) (_ E, _ bool) {
|
||||||
|
for {
|
||||||
|
if e, ok := err.(E); ok {
|
||||||
|
return e, true
|
||||||
|
}
|
||||||
|
if x, ok := err.(interface{ As(any) bool }); ok {
|
||||||
|
if *ppe == nil {
|
||||||
|
*ppe = new(E)
|
||||||
|
}
|
||||||
|
if x.As(*ppe) {
|
||||||
|
return **ppe, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch x := err.(type) {
|
||||||
|
case interface{ Unwrap() error }:
|
||||||
|
err = x.Unwrap()
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case interface{ Unwrap() []error }:
|
||||||
|
for _, err := range x.Unwrap() {
|
||||||
|
if err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if x, ok := asType(err, ppe); ok {
|
||||||
|
return x, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,123 @@ func TestAsValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAsType(t *testing.T) {
|
||||||
|
var errT errorT
|
||||||
|
var errP *fs.PathError
|
||||||
|
type timeout interface {
|
||||||
|
Timeout() bool
|
||||||
|
error
|
||||||
|
}
|
||||||
|
_, errF := os.Open("non-existing")
|
||||||
|
poserErr := &poser{"oh no", nil}
|
||||||
|
|
||||||
|
testAsType(t,
|
||||||
|
nil,
|
||||||
|
errP,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
wrapped{"pitied the fool", errorT{"T"}},
|
||||||
|
errorT{"T"},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
errF,
|
||||||
|
errF,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
errT,
|
||||||
|
errP,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
wrapped{"wrapped", nil},
|
||||||
|
errT,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
&poser{"error", nil},
|
||||||
|
errorT{"poser"},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
&poser{"path", nil},
|
||||||
|
poserPathErr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
poserErr,
|
||||||
|
poserErr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
errors.New("err"),
|
||||||
|
timeout(nil),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
errF,
|
||||||
|
errF.(timeout),
|
||||||
|
true)
|
||||||
|
testAsType(t,
|
||||||
|
wrapped{"path error", errF},
|
||||||
|
errF.(timeout),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{},
|
||||||
|
errT,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{errors.New("a"), errorT{"T"}},
|
||||||
|
errorT{"T"},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{errorT{"T"}, errors.New("a")},
|
||||||
|
errorT{"T"},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{errorT{"a"}, errorT{"b"}},
|
||||||
|
errorT{"a"},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}},
|
||||||
|
errorT{"a"},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{wrapped{"path error", errF}},
|
||||||
|
errF.(timeout),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
testAsType(t,
|
||||||
|
multiErr{nil},
|
||||||
|
errT,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type compError interface {
|
||||||
|
comparable
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAsType[E compError](t *testing.T, err error, want E, wantOK bool) {
|
||||||
|
t.Helper()
|
||||||
|
name := fmt.Sprintf("AsType[%T](Errorf(..., %v))", want, err)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
got, gotOK := errors.AsType[E](err)
|
||||||
|
if gotOK != wantOK || got != want {
|
||||||
|
t.Fatalf("got %v, %t; want %v, %t", got, gotOK, want, wantOK)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkIs(b *testing.B) {
|
func BenchmarkIs(b *testing.B) {
|
||||||
err1 := errors.New("1")
|
err1 := errors.New("1")
|
||||||
err2 := multiErr{multiErr{multiErr{err1, errorT{"a"}}, errorT{"b"}}}
|
err2 := multiErr{multiErr{multiErr{err1, errorT{"a"}}, errorT{"b"}}}
|
||||||
|
|
@ -260,6 +377,15 @@ func BenchmarkAs(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkAsType(b *testing.B) {
|
||||||
|
err := multiErr{multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}}}
|
||||||
|
for range b.N {
|
||||||
|
if _, ok := errors.AsType[errorT](err); !ok {
|
||||||
|
b.Fatal("AsType failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnwrap(t *testing.T) {
|
func TestUnwrap(t *testing.T) {
|
||||||
err1 := errors.New("1")
|
err1 := errors.New("1")
|
||||||
erra := wrapped{"wrap 2", err1}
|
erra := wrapped{"wrap 2", err1}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.42.0
|
golang.org/x/crypto v0.42.0
|
||||||
golang.org/x/net v0.44.0
|
golang.org/x/net v0.44.1-0.20251002015445-edb764c2296f
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
golang.org/x/net v0.44.1-0.20251002015445-edb764c2296f h1:vNklv+oJQSYNGsWXHoCPi2MHMcpj9/Q7aBhvvfnJvGg=
|
||||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
golang.org/x/net v0.44.1-0.20251002015445-edb764c2296f/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||||
|
|
|
||||||
|
|
@ -487,11 +487,16 @@ var depsRules = `
|
||||||
internal/byteorder < crypto/internal/fips140deps/byteorder;
|
internal/byteorder < crypto/internal/fips140deps/byteorder;
|
||||||
internal/cpu, internal/goarch < crypto/internal/fips140deps/cpu;
|
internal/cpu, internal/goarch < crypto/internal/fips140deps/cpu;
|
||||||
internal/godebug < crypto/internal/fips140deps/godebug;
|
internal/godebug < crypto/internal/fips140deps/godebug;
|
||||||
|
time, internal/syscall/windows < crypto/internal/fips140deps/time;
|
||||||
|
|
||||||
|
crypto/internal/fips140deps/time, errors, math/bits, sync/atomic, unsafe
|
||||||
|
< crypto/internal/fips140/entropy;
|
||||||
|
|
||||||
STR, hash,
|
STR, hash,
|
||||||
crypto/internal/impl,
|
crypto/internal/impl,
|
||||||
crypto/internal/entropy,
|
crypto/internal/entropy,
|
||||||
crypto/internal/randutil,
|
crypto/internal/randutil,
|
||||||
|
crypto/internal/fips140/entropy,
|
||||||
crypto/internal/fips140deps/byteorder,
|
crypto/internal/fips140deps/byteorder,
|
||||||
crypto/internal/fips140deps/cpu,
|
crypto/internal/fips140deps/cpu,
|
||||||
crypto/internal/fips140deps/godebug
|
crypto/internal/fips140deps/godebug
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ func gofips140() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFIPSVersion reports whether v is a valid FIPS version,
|
// isFIPSVersion reports whether v is a valid FIPS version,
|
||||||
// of the form vX.Y.Z.
|
// of the form vX.Y.Z or vX.Y.Z-hash.
|
||||||
func isFIPSVersion(v string) bool {
|
func isFIPSVersion(v string) bool {
|
||||||
if !strings.HasPrefix(v, "v") {
|
if !strings.HasPrefix(v, "v") {
|
||||||
return false
|
return false
|
||||||
|
|
@ -99,7 +99,8 @@ func isFIPSVersion(v string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
v, ok = skipNum(v[len("."):])
|
v, ok = skipNum(v[len("."):])
|
||||||
return ok && v == ""
|
hasHash := strings.HasPrefix(v, "-") && len(v) == len("-")+8
|
||||||
|
return ok && (v == "" || hasHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// skipNum skips the leading text matching [0-9]+
|
// skipNum skips the leading text matching [0-9]+
|
||||||
|
|
@ -320,18 +321,13 @@ func goriscv64() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
type gowasmFeatures struct {
|
type gowasmFeatures struct {
|
||||||
SatConv bool
|
// Legacy features, now always enabled
|
||||||
SignExt bool
|
//SatConv bool
|
||||||
|
//SignExt bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f gowasmFeatures) String() string {
|
func (f gowasmFeatures) String() string {
|
||||||
var flags []string
|
var flags []string
|
||||||
if f.SatConv {
|
|
||||||
flags = append(flags, "satconv")
|
|
||||||
}
|
|
||||||
if f.SignExt {
|
|
||||||
flags = append(flags, "signext")
|
|
||||||
}
|
|
||||||
return strings.Join(flags, ",")
|
return strings.Join(flags, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,9 +335,9 @@ func gowasm() (f gowasmFeatures) {
|
||||||
for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") {
|
for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") {
|
||||||
switch opt {
|
switch opt {
|
||||||
case "satconv":
|
case "satconv":
|
||||||
f.SatConv = true
|
// ignore, always enabled
|
||||||
case "signext":
|
case "signext":
|
||||||
f.SignExt = true
|
// ignore, always enabled
|
||||||
case "":
|
case "":
|
||||||
// ignore
|
// ignore
|
||||||
default:
|
default:
|
||||||
|
|
@ -451,12 +447,10 @@ func gogoarchTags() []string {
|
||||||
return list
|
return list
|
||||||
case "wasm":
|
case "wasm":
|
||||||
var list []string
|
var list []string
|
||||||
if GOWASM.SatConv {
|
// SatConv is always enabled
|
||||||
list = append(list, GOARCH+".satconv")
|
list = append(list, GOARCH+".satconv")
|
||||||
}
|
// SignExt is always enabled
|
||||||
if GOWASM.SignExt {
|
|
||||||
list = append(list, GOARCH+".signext")
|
list = append(list, GOARCH+".signext")
|
||||||
}
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Code generated by mkconsts.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !goexperiment.goroutineleakprofile
|
||||||
|
|
||||||
|
package goexperiment
|
||||||
|
|
||||||
|
const GoroutineLeakProfile = false
|
||||||
|
const GoroutineLeakProfileInt = 0
|
||||||
8
src/internal/goexperiment/exp_goroutineleakprofile_on.go
Normal file
8
src/internal/goexperiment/exp_goroutineleakprofile_on.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Code generated by mkconsts.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build goexperiment.goroutineleakprofile
|
||||||
|
|
||||||
|
package goexperiment
|
||||||
|
|
||||||
|
const GoroutineLeakProfile = true
|
||||||
|
const GoroutineLeakProfileInt = 1
|
||||||
|
|
@ -119,6 +119,9 @@ type Flags struct {
|
||||||
// SizeSpecializedMalloc enables malloc implementations that are specialized per size class.
|
// SizeSpecializedMalloc enables malloc implementations that are specialized per size class.
|
||||||
SizeSpecializedMalloc bool
|
SizeSpecializedMalloc bool
|
||||||
|
|
||||||
|
// GoroutineLeakProfile enables the collection of goroutine leak profiles.
|
||||||
|
GoroutineLeakProfile bool
|
||||||
|
|
||||||
// SIMD enables the simd package and the compiler's handling
|
// SIMD enables the simd package and the compiler's handling
|
||||||
// of SIMD intrinsics.
|
// of SIMD intrinsics.
|
||||||
SIMD bool
|
SIMD bool
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"internal/race"
|
"internal/race"
|
||||||
"internal/syscall/windows"
|
"internal/syscall/windows"
|
||||||
"io"
|
"io"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
@ -75,25 +76,6 @@ type operation struct {
|
||||||
// fields used by runtime.netpoll
|
// fields used by runtime.netpoll
|
||||||
runtimeCtx uintptr
|
runtimeCtx uintptr
|
||||||
mode int32
|
mode int32
|
||||||
|
|
||||||
// fields used only by net package
|
|
||||||
buf syscall.WSABuf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *operation) setEvent() {
|
|
||||||
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
|
||||||
if err != nil {
|
|
||||||
// This shouldn't happen when all CreateEvent arguments are zero.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Set the low bit so that the external IOCP doesn't receive the completion packet.
|
|
||||||
o.o.HEvent = h | 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *operation) close() {
|
|
||||||
if o.o.HEvent != 0 {
|
|
||||||
syscall.CloseHandle(o.o.HEvent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fd *FD) overlapped(o *operation) *syscall.Overlapped {
|
func (fd *FD) overlapped(o *operation) *syscall.Overlapped {
|
||||||
|
|
@ -107,9 +89,8 @@ func (fd *FD) overlapped(o *operation) *syscall.Overlapped {
|
||||||
return &o.o
|
return &o.o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *operation) InitBuf(buf []byte) {
|
func newWsaBuf(b []byte) *syscall.WSABuf {
|
||||||
o.buf.Len = uint32(len(buf))
|
return &syscall.WSABuf{Buf: unsafe.SliceData(b), Len: uint32(len(b))}
|
||||||
o.buf.Buf = unsafe.SliceData(buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wsaBufsPool = sync.Pool{
|
var wsaBufsPool = sync.Pool{
|
||||||
|
|
@ -168,7 +149,7 @@ var wsaMsgPool = sync.Pool{
|
||||||
|
|
||||||
// newWSAMsg creates a new WSAMsg with the provided parameters.
|
// newWSAMsg creates a new WSAMsg with the provided parameters.
|
||||||
// Use [freeWSAMsg] to free it.
|
// Use [freeWSAMsg] to free it.
|
||||||
func newWSAMsg(p []byte, oob []byte, flags int, rsa *syscall.RawSockaddrAny) *windows.WSAMsg {
|
func newWSAMsg(p []byte, oob []byte, flags int, unconnected bool) *windows.WSAMsg {
|
||||||
// The returned object can't be allocated in the stack because it is accessed asynchronously
|
// The returned object can't be allocated in the stack because it is accessed asynchronously
|
||||||
// by Windows in between several system calls. If the stack frame is moved while that happens,
|
// by Windows in between several system calls. If the stack frame is moved while that happens,
|
||||||
// then Windows may access invalid memory.
|
// then Windows may access invalid memory.
|
||||||
|
|
@ -183,11 +164,9 @@ func newWSAMsg(p []byte, oob []byte, flags int, rsa *syscall.RawSockaddrAny) *wi
|
||||||
Buf: unsafe.SliceData(oob),
|
Buf: unsafe.SliceData(oob),
|
||||||
}
|
}
|
||||||
msg.Flags = uint32(flags)
|
msg.Flags = uint32(flags)
|
||||||
msg.Name = syscall.Pointer(unsafe.Pointer(rsa))
|
if unconnected {
|
||||||
if rsa != nil {
|
msg.Name = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||||
msg.Namelen = int32(unsafe.Sizeof(*rsa))
|
msg.Namelen = int32(unsafe.Sizeof(syscall.RawSockaddrAny{}))
|
||||||
} else {
|
|
||||||
msg.Namelen = 0
|
|
||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
@ -198,6 +177,12 @@ func freeWSAMsg(msg *windows.WSAMsg) {
|
||||||
msg.Buffers.Buf = nil
|
msg.Buffers.Buf = nil
|
||||||
msg.Control.Len = 0
|
msg.Control.Len = 0
|
||||||
msg.Control.Buf = nil
|
msg.Control.Buf = nil
|
||||||
|
if msg.Name != nil {
|
||||||
|
*msg.Name = syscall.RawSockaddrAny{}
|
||||||
|
wsaRsaPool.Put(msg.Name)
|
||||||
|
msg.Name = nil
|
||||||
|
msg.Namelen = 0
|
||||||
|
}
|
||||||
wsaMsgPool.Put(msg)
|
wsaMsgPool.Put(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,6 +192,12 @@ var wsaRsaPool = sync.Pool{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var operationPool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return new(operation)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// waitIO waits for the IO operation o to complete.
|
// waitIO waits for the IO operation o to complete.
|
||||||
func (fd *FD) waitIO(o *operation) error {
|
func (fd *FD) waitIO(o *operation) error {
|
||||||
if fd.isBlocking {
|
if fd.isBlocking {
|
||||||
|
|
@ -245,23 +236,57 @@ func (fd *FD) cancelIO(o *operation) {
|
||||||
fd.pd.waitCanceled(int(o.mode))
|
fd.pd.waitCanceled(int(o.mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pin pins ptr for the duration of the IO operation.
|
||||||
|
// If fd is in blocking mode, pin does nothing.
|
||||||
|
func (fd *FD) pin(mode int, ptr any) {
|
||||||
|
if fd.isBlocking {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mode == 'r' {
|
||||||
|
fd.readPinner.Pin(ptr)
|
||||||
|
} else {
|
||||||
|
fd.writePinner.Pin(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// execIO executes a single IO operation o.
|
// execIO executes a single IO operation o.
|
||||||
// It supports both synchronous and asynchronous IO.
|
// It supports both synchronous and asynchronous IO.
|
||||||
// o.qty and o.flags are set to zero before calling submit
|
func (fd *FD) execIO(mode int, submit func(o *operation) (uint32, error)) (int, error) {
|
||||||
// to avoid reusing the values from a previous call.
|
if mode == 'r' {
|
||||||
func (fd *FD) execIO(o *operation, submit func(o *operation) (uint32, error)) (int, error) {
|
defer fd.readPinner.Unpin()
|
||||||
|
} else {
|
||||||
|
defer fd.writePinner.Unpin()
|
||||||
|
}
|
||||||
// Notify runtime netpoll about starting IO.
|
// Notify runtime netpoll about starting IO.
|
||||||
err := fd.pd.prepare(int(o.mode), fd.isFile)
|
err := fd.pd.prepare(mode, fd.isFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
o := operationPool.Get().(*operation)
|
||||||
|
defer operationPool.Put(o)
|
||||||
|
*o = operation{
|
||||||
|
o: syscall.Overlapped{
|
||||||
|
OffsetHigh: uint32(fd.offset >> 32),
|
||||||
|
Offset: uint32(fd.offset),
|
||||||
|
},
|
||||||
|
runtimeCtx: fd.pd.runtimeCtx,
|
||||||
|
mode: int32(mode),
|
||||||
|
}
|
||||||
// Start IO.
|
// Start IO.
|
||||||
if !fd.isBlocking && o.o.HEvent == 0 && !fd.pollable() {
|
if !fd.isBlocking && !fd.pollable() {
|
||||||
// If the handle is opened for overlapped IO but we can't
|
// If the handle is opened for overlapped IO but we can't
|
||||||
// use the runtime poller, then we need to use an
|
// use the runtime poller, then we need to use an
|
||||||
// event to wait for the IO to complete.
|
// event to wait for the IO to complete.
|
||||||
o.setEvent()
|
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
// This shouldn't happen when all CreateEvent arguments are zero.
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// Set the low bit so that the external IOCP doesn't receive the completion packet.
|
||||||
|
o.o.HEvent = h | 1
|
||||||
|
defer syscall.CloseHandle(h)
|
||||||
|
}
|
||||||
|
fd.pin(mode, o)
|
||||||
qty, err := submit(o)
|
qty, err := submit(o)
|
||||||
var waitErr error
|
var waitErr error
|
||||||
// Blocking operations shouldn't return ERROR_IO_PENDING.
|
// Blocking operations shouldn't return ERROR_IO_PENDING.
|
||||||
|
|
@ -316,11 +341,6 @@ type FD struct {
|
||||||
// System file descriptor. Immutable until Close.
|
// System file descriptor. Immutable until Close.
|
||||||
Sysfd syscall.Handle
|
Sysfd syscall.Handle
|
||||||
|
|
||||||
// Read operation.
|
|
||||||
rop operation
|
|
||||||
// Write operation.
|
|
||||||
wop operation
|
|
||||||
|
|
||||||
// I/O poller.
|
// I/O poller.
|
||||||
pd pollDesc
|
pd pollDesc
|
||||||
|
|
||||||
|
|
@ -358,6 +378,11 @@ type FD struct {
|
||||||
isBlocking bool
|
isBlocking bool
|
||||||
|
|
||||||
disassociated atomic.Bool
|
disassociated atomic.Bool
|
||||||
|
|
||||||
|
// readPinner and writePinner are automatically unpinned
|
||||||
|
// before execIO returns.
|
||||||
|
readPinner runtime.Pinner
|
||||||
|
writePinner runtime.Pinner
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOffset sets the offset fields of the overlapped object
|
// setOffset sets the offset fields of the overlapped object
|
||||||
|
|
@ -375,8 +400,6 @@ type FD struct {
|
||||||
// using an external mechanism.
|
// using an external mechanism.
|
||||||
func (fd *FD) setOffset(off int64) {
|
func (fd *FD) setOffset(off int64) {
|
||||||
fd.offset = off
|
fd.offset = off
|
||||||
fd.rop.o.OffsetHigh, fd.rop.o.Offset = uint32(off>>32), uint32(off)
|
|
||||||
fd.wop.o.OffsetHigh, fd.wop.o.Offset = uint32(off>>32), uint32(off)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addOffset adds the given offset to the current offset.
|
// addOffset adds the given offset to the current offset.
|
||||||
|
|
@ -427,8 +450,6 @@ func (fd *FD) Init(net string, pollable bool) error {
|
||||||
}
|
}
|
||||||
fd.isFile = fd.kind != kindNet
|
fd.isFile = fd.kind != kindNet
|
||||||
fd.isBlocking = !pollable
|
fd.isBlocking = !pollable
|
||||||
fd.rop.mode = 'r'
|
|
||||||
fd.wop.mode = 'w'
|
|
||||||
|
|
||||||
// It is safe to add overlapped handles that also perform I/O
|
// It is safe to add overlapped handles that also perform I/O
|
||||||
// outside of the runtime poller. The runtime poller will ignore
|
// outside of the runtime poller. The runtime poller will ignore
|
||||||
|
|
@ -437,8 +458,6 @@ func (fd *FD) Init(net string, pollable bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fd.rop.runtimeCtx = fd.pd.runtimeCtx
|
|
||||||
fd.wop.runtimeCtx = fd.pd.runtimeCtx
|
|
||||||
if fd.kind != kindNet || socketCanUseSetFileCompletionNotificationModes {
|
if fd.kind != kindNet || socketCanUseSetFileCompletionNotificationModes {
|
||||||
// Non-socket handles can use SetFileCompletionNotificationModes without problems.
|
// Non-socket handles can use SetFileCompletionNotificationModes without problems.
|
||||||
err := syscall.SetFileCompletionNotificationModes(fd.Sysfd,
|
err := syscall.SetFileCompletionNotificationModes(fd.Sysfd,
|
||||||
|
|
@ -477,8 +496,6 @@ func (fd *FD) destroy() error {
|
||||||
if fd.Sysfd == syscall.InvalidHandle {
|
if fd.Sysfd == syscall.InvalidHandle {
|
||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
fd.rop.close()
|
|
||||||
fd.wop.close()
|
|
||||||
// Poller may want to unregister fd in readiness notification mechanism,
|
// Poller may want to unregister fd in readiness notification mechanism,
|
||||||
// so this must be executed before fd.CloseFunc.
|
// so this must be executed before fd.CloseFunc.
|
||||||
fd.pd.close()
|
fd.pd.close()
|
||||||
|
|
@ -533,6 +550,10 @@ func (fd *FD) Read(buf []byte) (int, error) {
|
||||||
defer fd.readUnlock()
|
defer fd.readUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(buf) > 0 {
|
||||||
|
fd.pin('r', &buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
if len(buf) > maxRW {
|
if len(buf) > maxRW {
|
||||||
buf = buf[:maxRW]
|
buf = buf[:maxRW]
|
||||||
}
|
}
|
||||||
|
|
@ -543,10 +564,8 @@ func (fd *FD) Read(buf []byte) (int, error) {
|
||||||
case kindConsole:
|
case kindConsole:
|
||||||
n, err = fd.readConsole(buf)
|
n, err = fd.readConsole(buf)
|
||||||
case kindFile, kindPipe:
|
case kindFile, kindPipe:
|
||||||
o := &fd.rop
|
n, err = fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(buf)
|
err = syscall.ReadFile(fd.Sysfd, buf, &qty, fd.overlapped(o))
|
||||||
n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = syscall.ReadFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, fd.overlapped(o))
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
fd.addOffset(n)
|
fd.addOffset(n)
|
||||||
|
|
@ -560,11 +579,9 @@ func (fd *FD) Read(buf []byte) (int, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case kindNet:
|
case kindNet:
|
||||||
o := &fd.rop
|
n, err = fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(buf)
|
|
||||||
n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
var flags uint32
|
var flags uint32
|
||||||
err = syscall.WSARecv(fd.Sysfd, &o.buf, 1, &qty, &flags, &o.o, nil)
|
err = syscall.WSARecv(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
if race.Enabled {
|
if race.Enabled {
|
||||||
|
|
@ -652,7 +669,7 @@ func (fd *FD) readConsole(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pread emulates the Unix pread system call.
|
// Pread emulates the Unix pread system call.
|
||||||
func (fd *FD) Pread(b []byte, off int64) (int, error) {
|
func (fd *FD) Pread(buf []byte, off int64) (int, error) {
|
||||||
if fd.kind == kindPipe {
|
if fd.kind == kindPipe {
|
||||||
// Pread does not work with pipes
|
// Pread does not work with pipes
|
||||||
return 0, syscall.ESPIPE
|
return 0, syscall.ESPIPE
|
||||||
|
|
@ -663,8 +680,12 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) {
|
||||||
}
|
}
|
||||||
defer fd.readWriteUnlock()
|
defer fd.readWriteUnlock()
|
||||||
|
|
||||||
if len(b) > maxRW {
|
if len(buf) > 0 {
|
||||||
b = b[:maxRW]
|
fd.pin('r', &buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) > maxRW {
|
||||||
|
buf = buf[:maxRW]
|
||||||
}
|
}
|
||||||
|
|
||||||
if fd.isBlocking {
|
if fd.isBlocking {
|
||||||
|
|
@ -683,17 +704,15 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) {
|
||||||
curoffset := fd.offset
|
curoffset := fd.offset
|
||||||
defer fd.setOffset(curoffset)
|
defer fd.setOffset(curoffset)
|
||||||
}
|
}
|
||||||
o := &fd.rop
|
|
||||||
o.InitBuf(b)
|
|
||||||
fd.setOffset(off)
|
fd.setOffset(off)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
err = syscall.ReadFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, &o.o)
|
err = syscall.ReadFile(fd.Sysfd, buf, &qty, &o.o)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
if err == syscall.ERROR_HANDLE_EOF {
|
if err == syscall.ERROR_HANDLE_EOF {
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
}
|
}
|
||||||
if len(b) != 0 {
|
if len(buf) != 0 {
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
}
|
}
|
||||||
return n, err
|
return n, err
|
||||||
|
|
@ -711,14 +730,15 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
defer fd.readUnlock()
|
defer fd.readUnlock()
|
||||||
o := &fd.rop
|
|
||||||
o.InitBuf(buf)
|
fd.pin('r', &buf[0])
|
||||||
|
|
||||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||||
defer wsaRsaPool.Put(rsa)
|
defer wsaRsaPool.Put(rsa)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
rsan := int32(unsafe.Sizeof(*rsa))
|
rsan := int32(unsafe.Sizeof(*rsa))
|
||||||
var flags uint32
|
var flags uint32
|
||||||
err = syscall.WSARecvFrom(fd.Sysfd, &o.buf, 1, &qty, &flags, rsa, &rsan, &o.o, nil)
|
err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, rsa, &rsan, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
|
|
@ -741,14 +761,15 @@ func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer fd.readUnlock()
|
defer fd.readUnlock()
|
||||||
o := &fd.rop
|
|
||||||
o.InitBuf(buf)
|
fd.pin('r', &buf[0])
|
||||||
|
|
||||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||||
defer wsaRsaPool.Put(rsa)
|
defer wsaRsaPool.Put(rsa)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
rsan := int32(unsafe.Sizeof(*rsa))
|
rsan := int32(unsafe.Sizeof(*rsa))
|
||||||
var flags uint32
|
var flags uint32
|
||||||
err = syscall.WSARecvFrom(fd.Sysfd, &o.buf, 1, &qty, &flags, rsa, &rsan, &o.o, nil)
|
err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, rsa, &rsan, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
|
|
@ -771,14 +792,15 @@ func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer fd.readUnlock()
|
defer fd.readUnlock()
|
||||||
o := &fd.rop
|
|
||||||
o.InitBuf(buf)
|
fd.pin('r', &buf[0])
|
||||||
|
|
||||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||||
defer wsaRsaPool.Put(rsa)
|
defer wsaRsaPool.Put(rsa)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
rsan := int32(unsafe.Sizeof(*rsa))
|
rsan := int32(unsafe.Sizeof(*rsa))
|
||||||
var flags uint32
|
var flags uint32
|
||||||
err = syscall.WSARecvFrom(fd.Sysfd, &o.buf, 1, &qty, &flags, rsa, &rsan, &o.o, nil)
|
err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, rsa, &rsan, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
|
|
@ -803,6 +825,9 @@ func (fd *FD) Write(buf []byte) (int, error) {
|
||||||
defer fd.writeUnlock()
|
defer fd.writeUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(buf) > 0 {
|
||||||
|
fd.pin('w', &buf[0])
|
||||||
|
}
|
||||||
var ntotal int
|
var ntotal int
|
||||||
for {
|
for {
|
||||||
max := len(buf)
|
max := len(buf)
|
||||||
|
|
@ -816,10 +841,8 @@ func (fd *FD) Write(buf []byte) (int, error) {
|
||||||
case kindConsole:
|
case kindConsole:
|
||||||
n, err = fd.writeConsole(b)
|
n, err = fd.writeConsole(b)
|
||||||
case kindPipe, kindFile:
|
case kindPipe, kindFile:
|
||||||
o := &fd.wop
|
n, err = fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(b)
|
err = syscall.WriteFile(fd.Sysfd, b, &qty, fd.overlapped(o))
|
||||||
n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = syscall.WriteFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, fd.overlapped(o))
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
fd.addOffset(n)
|
fd.addOffset(n)
|
||||||
|
|
@ -827,10 +850,8 @@ func (fd *FD) Write(buf []byte) (int, error) {
|
||||||
if race.Enabled {
|
if race.Enabled {
|
||||||
race.ReleaseMerge(unsafe.Pointer(&ioSync))
|
race.ReleaseMerge(unsafe.Pointer(&ioSync))
|
||||||
}
|
}
|
||||||
o := &fd.wop
|
n, err = fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(b)
|
err = syscall.WSASend(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, &o.o, nil)
|
||||||
n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = syscall.WSASend(fd.Sysfd, &o.buf, 1, &qty, 0, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -899,6 +920,10 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) {
|
||||||
}
|
}
|
||||||
defer fd.readWriteUnlock()
|
defer fd.readWriteUnlock()
|
||||||
|
|
||||||
|
if len(buf) > 0 {
|
||||||
|
fd.pin('w', &buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
if fd.isBlocking {
|
if fd.isBlocking {
|
||||||
curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
|
curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -922,12 +947,9 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) {
|
||||||
if max-ntotal > maxRW {
|
if max-ntotal > maxRW {
|
||||||
max = ntotal + maxRW
|
max = ntotal + maxRW
|
||||||
}
|
}
|
||||||
b := buf[ntotal:max]
|
|
||||||
o := &fd.wop
|
|
||||||
o.InitBuf(b)
|
|
||||||
fd.setOffset(off + int64(ntotal))
|
fd.setOffset(off + int64(ntotal))
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
err = syscall.WriteFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, &o.o)
|
err = syscall.WriteFile(fd.Sysfd, buf[ntotal:max], &qty, &o.o)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
|
@ -956,7 +978,7 @@ func (fd *FD) Writev(buf *[][]byte) (int64, error) {
|
||||||
}
|
}
|
||||||
bufs := newWSABufs(buf)
|
bufs := newWSABufs(buf)
|
||||||
defer freeWSABufs(bufs)
|
defer freeWSABufs(bufs)
|
||||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
err = syscall.WSASend(fd.Sysfd, &(*bufs)[0], uint32(len(*bufs)), &qty, 0, &o.o, nil)
|
err = syscall.WSASend(fd.Sysfd, &(*bufs)[0], uint32(len(*bufs)), &qty, 0, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
|
|
@ -974,25 +996,23 @@ func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
|
||||||
|
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
// handle zero-byte payload
|
// handle zero-byte payload
|
||||||
o := &fd.wop
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(buf)
|
err = syscall.WSASendto(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa, &o.o, nil)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = syscall.WSASendto(fd.Sysfd, &o.buf, 1, &qty, 0, sa, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fd.pin('w', &buf[0])
|
||||||
|
|
||||||
ntotal := 0
|
ntotal := 0
|
||||||
for len(buf) > 0 {
|
for len(buf) > 0 {
|
||||||
b := buf
|
b := buf
|
||||||
if len(b) > maxRW {
|
if len(b) > maxRW {
|
||||||
b = b[:maxRW]
|
b = b[:maxRW]
|
||||||
}
|
}
|
||||||
o := &fd.wop
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(b)
|
err = syscall.WSASendto(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa, &o.o, nil)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = syscall.WSASendto(fd.Sysfd, &o.buf, 1, &qty, 0, sa, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
ntotal += int(n)
|
ntotal += int(n)
|
||||||
|
|
@ -1013,25 +1033,23 @@ func (fd *FD) WriteToInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error)
|
||||||
|
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
// handle zero-byte payload
|
// handle zero-byte payload
|
||||||
o := &fd.wop
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(buf)
|
err = windows.WSASendtoInet4(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa4, &o.o, nil)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = windows.WSASendtoInet4(fd.Sysfd, &o.buf, 1, &qty, 0, sa4, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fd.pin('w', &buf[0])
|
||||||
|
|
||||||
ntotal := 0
|
ntotal := 0
|
||||||
for len(buf) > 0 {
|
for len(buf) > 0 {
|
||||||
b := buf
|
b := buf
|
||||||
if len(b) > maxRW {
|
if len(b) > maxRW {
|
||||||
b = b[:maxRW]
|
b = b[:maxRW]
|
||||||
}
|
}
|
||||||
o := &fd.wop
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(b)
|
err = windows.WSASendtoInet4(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa4, &o.o, nil)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = windows.WSASendtoInet4(fd.Sysfd, &o.buf, 1, &qty, 0, sa4, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
ntotal += int(n)
|
ntotal += int(n)
|
||||||
|
|
@ -1052,25 +1070,23 @@ func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)
|
||||||
|
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
// handle zero-byte payload
|
// handle zero-byte payload
|
||||||
o := &fd.wop
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(buf)
|
err = windows.WSASendtoInet6(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa6, &o.o, nil)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = windows.WSASendtoInet6(fd.Sysfd, &o.buf, 1, &qty, 0, sa6, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fd.pin('w', &buf[0])
|
||||||
|
|
||||||
ntotal := 0
|
ntotal := 0
|
||||||
for len(buf) > 0 {
|
for len(buf) > 0 {
|
||||||
b := buf
|
b := buf
|
||||||
if len(b) > maxRW {
|
if len(b) > maxRW {
|
||||||
b = b[:maxRW]
|
b = b[:maxRW]
|
||||||
}
|
}
|
||||||
o := &fd.wop
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(b)
|
err = windows.WSASendtoInet6(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa6, &o.o, nil)
|
||||||
n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
err = windows.WSASendtoInet6(fd.Sysfd, &o.buf, 1, &qty, 0, sa6, &o.o, nil)
|
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
ntotal += int(n)
|
ntotal += int(n)
|
||||||
|
|
@ -1086,17 +1102,16 @@ func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)
|
||||||
// called when the descriptor is first created. This is here rather
|
// called when the descriptor is first created. This is here rather
|
||||||
// than in the net package so that it can use fd.wop.
|
// than in the net package so that it can use fd.wop.
|
||||||
func (fd *FD) ConnectEx(ra syscall.Sockaddr) error {
|
func (fd *FD) ConnectEx(ra syscall.Sockaddr) error {
|
||||||
o := &fd.wop
|
_, err := fd.execIO('w', func(o *operation) (uint32, error) {
|
||||||
_, err := fd.execIO(o, func(o *operation) (uint32, error) {
|
|
||||||
return 0, ConnectExFunc(fd.Sysfd, ra, nil, 0, nil, &o.o)
|
return 0, ConnectExFunc(fd.Sysfd, ra, nil, 0, nil, &o.o)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny, o *operation) (string, error) {
|
func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny) (string, error) {
|
||||||
// Submit accept request.
|
// Submit accept request.
|
||||||
rsan := uint32(unsafe.Sizeof(rawsa[0]))
|
rsan := uint32(unsafe.Sizeof(rawsa[0]))
|
||||||
_, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
_, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
err = AcceptFunc(fd.Sysfd, s, (*byte)(unsafe.Pointer(&rawsa[0])), 0, rsan, rsan, &qty, &o.o)
|
err = AcceptFunc(fd.Sysfd, s, (*byte)(unsafe.Pointer(&rawsa[0])), 0, rsan, rsan, &qty, &o.o)
|
||||||
return qty, err
|
return qty, err
|
||||||
|
|
||||||
|
|
@ -1124,7 +1139,6 @@ func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle,
|
||||||
}
|
}
|
||||||
defer fd.readUnlock()
|
defer fd.readUnlock()
|
||||||
|
|
||||||
o := &fd.rop
|
|
||||||
var rawsa [2]syscall.RawSockaddrAny
|
var rawsa [2]syscall.RawSockaddrAny
|
||||||
for {
|
for {
|
||||||
s, err := sysSocket()
|
s, err := sysSocket()
|
||||||
|
|
@ -1132,7 +1146,7 @@ func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle,
|
||||||
return syscall.InvalidHandle, nil, 0, "", err
|
return syscall.InvalidHandle, nil, 0, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
errcall, err := fd.acceptOne(s, rawsa[:], o)
|
errcall, err := fd.acceptOne(s, rawsa[:])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return s, rawsa[:], uint32(unsafe.Sizeof(rawsa[0])), "", nil
|
return s, rawsa[:], uint32(unsafe.Sizeof(rawsa[0])), "", nil
|
||||||
}
|
}
|
||||||
|
|
@ -1165,11 +1179,29 @@ func (fd *FD) Seek(offset int64, whence int) (int64, error) {
|
||||||
}
|
}
|
||||||
defer fd.readWriteUnlock()
|
defer fd.readWriteUnlock()
|
||||||
|
|
||||||
if !fd.isBlocking && whence == io.SeekCurrent {
|
if !fd.isBlocking {
|
||||||
// Windows doesn't keep the file pointer for overlapped file handles.
|
// Windows doesn't use the file pointer for overlapped file handles,
|
||||||
// We do it ourselves in case to account for any read or write
|
// there is no point on calling syscall.Seek.
|
||||||
// operations that may have occurred.
|
var newOffset int64
|
||||||
offset += fd.offset
|
switch whence {
|
||||||
|
case io.SeekStart:
|
||||||
|
newOffset = offset
|
||||||
|
case io.SeekCurrent:
|
||||||
|
newOffset = fd.offset + offset
|
||||||
|
case io.SeekEnd:
|
||||||
|
var size int64
|
||||||
|
if err := windows.GetFileSizeEx(fd.Sysfd, &size); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
newOffset = size + offset
|
||||||
|
default:
|
||||||
|
return 0, windows.ERROR_INVALID_PARAMETER
|
||||||
|
}
|
||||||
|
if newOffset < 0 {
|
||||||
|
return 0, windows.ERROR_NEGATIVE_SEEK
|
||||||
|
}
|
||||||
|
fd.setOffset(newOffset)
|
||||||
|
return newOffset, nil
|
||||||
}
|
}
|
||||||
n, err := syscall.Seek(fd.Sysfd, offset, whence)
|
n, err := syscall.Seek(fd.Sysfd, offset, whence)
|
||||||
fd.setOffset(n)
|
fd.setOffset(n)
|
||||||
|
|
@ -1242,14 +1274,12 @@ func (fd *FD) RawRead(f func(uintptr) bool) error {
|
||||||
|
|
||||||
// Use a zero-byte read as a way to get notified when this
|
// Use a zero-byte read as a way to get notified when this
|
||||||
// socket is readable. h/t https://stackoverflow.com/a/42019668/332798
|
// socket is readable. h/t https://stackoverflow.com/a/42019668/332798
|
||||||
o := &fd.rop
|
_, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
o.InitBuf(nil)
|
|
||||||
_, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
|
||||||
var flags uint32
|
var flags uint32
|
||||||
if !fd.IsStream {
|
if !fd.IsStream {
|
||||||
flags |= windows.MSG_PEEK
|
flags |= windows.MSG_PEEK
|
||||||
}
|
}
|
||||||
err = syscall.WSARecv(fd.Sysfd, &o.buf, 1, &qty, &flags, &o.o, nil)
|
err = syscall.WSARecv(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, &flags, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
if err == windows.WSAEMSGSIZE {
|
if err == windows.WSAEMSGSIZE {
|
||||||
|
|
@ -1337,18 +1367,16 @@ func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.S
|
||||||
p = p[:maxRW]
|
p = p[:maxRW]
|
||||||
}
|
}
|
||||||
|
|
||||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
msg := newWSAMsg(p, oob, flags, true)
|
||||||
defer wsaRsaPool.Put(rsa)
|
|
||||||
msg := newWSAMsg(p, oob, flags, rsa)
|
|
||||||
defer freeWSAMsg(msg)
|
defer freeWSAMsg(msg)
|
||||||
n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
var sa syscall.Sockaddr
|
var sa syscall.Sockaddr
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sa, err = rsa.Sockaddr()
|
sa, err = msg.Name.Sockaddr()
|
||||||
}
|
}
|
||||||
return n, int(msg.Control.Len), int(msg.Flags), sa, err
|
return n, int(msg.Control.Len), int(msg.Flags), sa, err
|
||||||
}
|
}
|
||||||
|
|
@ -1364,17 +1392,15 @@ func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.Sockadd
|
||||||
p = p[:maxRW]
|
p = p[:maxRW]
|
||||||
}
|
}
|
||||||
|
|
||||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
msg := newWSAMsg(p, oob, flags, true)
|
||||||
defer wsaRsaPool.Put(rsa)
|
|
||||||
msg := newWSAMsg(p, oob, flags, rsa)
|
|
||||||
defer freeWSAMsg(msg)
|
defer freeWSAMsg(msg)
|
||||||
n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawToSockaddrInet4(rsa, sa4)
|
rawToSockaddrInet4(msg.Name, sa4)
|
||||||
}
|
}
|
||||||
return n, int(msg.Control.Len), int(msg.Flags), err
|
return n, int(msg.Control.Len), int(msg.Flags), err
|
||||||
}
|
}
|
||||||
|
|
@ -1390,17 +1416,15 @@ func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.Sockadd
|
||||||
p = p[:maxRW]
|
p = p[:maxRW]
|
||||||
}
|
}
|
||||||
|
|
||||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
msg := newWSAMsg(p, oob, flags, true)
|
||||||
defer wsaRsaPool.Put(rsa)
|
|
||||||
msg := newWSAMsg(p, oob, flags, rsa)
|
|
||||||
defer freeWSAMsg(msg)
|
defer freeWSAMsg(msg)
|
||||||
n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||||
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
err = fd.eofError(n, err)
|
err = fd.eofError(n, err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawToSockaddrInet6(rsa, sa6)
|
rawToSockaddrInet6(msg.Name, sa6)
|
||||||
}
|
}
|
||||||
return n, int(msg.Control.Len), int(msg.Flags), err
|
return n, int(msg.Control.Len), int(msg.Flags), err
|
||||||
}
|
}
|
||||||
|
|
@ -1416,21 +1440,16 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err
|
||||||
}
|
}
|
||||||
defer fd.writeUnlock()
|
defer fd.writeUnlock()
|
||||||
|
|
||||||
var rsa *syscall.RawSockaddrAny
|
msg := newWSAMsg(p, oob, 0, sa != nil)
|
||||||
if sa != nil {
|
|
||||||
rsa = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
|
||||||
defer wsaRsaPool.Put(rsa)
|
|
||||||
}
|
|
||||||
msg := newWSAMsg(p, oob, 0, rsa)
|
|
||||||
defer freeWSAMsg(msg)
|
defer freeWSAMsg(msg)
|
||||||
if sa != nil {
|
if sa != nil {
|
||||||
var err error
|
var err error
|
||||||
msg.Namelen, err = sockaddrToRaw(rsa, sa)
|
msg.Namelen, err = sockaddrToRaw(msg.Name, sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
|
|
@ -1448,17 +1467,12 @@ func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (in
|
||||||
}
|
}
|
||||||
defer fd.writeUnlock()
|
defer fd.writeUnlock()
|
||||||
|
|
||||||
var rsa *syscall.RawSockaddrAny
|
msg := newWSAMsg(p, oob, 0, sa != nil)
|
||||||
if sa != nil {
|
|
||||||
rsa = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
|
||||||
defer wsaRsaPool.Put(rsa)
|
|
||||||
}
|
|
||||||
msg := newWSAMsg(p, oob, 0, rsa)
|
|
||||||
defer freeWSAMsg(msg)
|
defer freeWSAMsg(msg)
|
||||||
if sa != nil {
|
if sa != nil {
|
||||||
msg.Namelen = sockaddrInet4ToRaw(rsa, sa)
|
msg.Namelen = sockaddrInet4ToRaw(msg.Name, sa)
|
||||||
}
|
}
|
||||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
|
|
@ -1476,17 +1490,12 @@ func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (in
|
||||||
}
|
}
|
||||||
defer fd.writeUnlock()
|
defer fd.writeUnlock()
|
||||||
|
|
||||||
var rsa *syscall.RawSockaddrAny
|
msg := newWSAMsg(p, oob, 0, sa != nil)
|
||||||
if sa != nil {
|
|
||||||
rsa = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
|
||||||
defer wsaRsaPool.Put(rsa)
|
|
||||||
}
|
|
||||||
msg := newWSAMsg(p, oob, 0, rsa)
|
|
||||||
defer freeWSAMsg(msg)
|
defer freeWSAMsg(msg)
|
||||||
if sa != nil {
|
if sa != nil {
|
||||||
msg.Namelen = sockaddrInet6ToRaw(rsa, sa)
|
msg.Namelen = sockaddrInet6ToRaw(msg.Name, sa)
|
||||||
}
|
}
|
||||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||||
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
||||||
return qty, err
|
return qty, err
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -62,18 +62,14 @@ func SendFile(fd *FD, src uintptr, size int64) (written int64, err error, handle
|
||||||
// See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
|
// See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
|
||||||
const maxChunkSizePerCall = int64(0x7fffffff - 1)
|
const maxChunkSizePerCall = int64(0x7fffffff - 1)
|
||||||
|
|
||||||
o := &fd.wop
|
|
||||||
for size > 0 {
|
for size > 0 {
|
||||||
chunkSize := maxChunkSizePerCall
|
chunkSize := maxChunkSizePerCall
|
||||||
if chunkSize > size {
|
if chunkSize > size {
|
||||||
chunkSize = size
|
chunkSize = size
|
||||||
}
|
}
|
||||||
|
|
||||||
off := startpos + written
|
fd.setOffset(startpos + written)
|
||||||
o.o.Offset = uint32(off)
|
n, err := fd.execIO('w', func(o *operation) (uint32, error) {
|
||||||
o.o.OffsetHigh = uint32(off >> 32)
|
|
||||||
|
|
||||||
n, err := fd.execIO(o, func(o *operation) (uint32, error) {
|
|
||||||
err := syscall.TransmitFile(fd.Sysfd, hsrc, uint32(chunkSize), 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
|
err := syscall.TransmitFile(fd.Sysfd, hsrc, uint32(chunkSize), 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import "internal/goarch"
|
||||||
|
|
||||||
// ObjMask is a bitmap where each bit corresponds to an object in a span.
|
// ObjMask is a bitmap where each bit corresponds to an object in a span.
|
||||||
//
|
//
|
||||||
// It is sized to accomodate all size classes.
|
// It is sized to accommodate all size classes.
|
||||||
type ObjMask [MaxObjsPerSpan / (goarch.PtrSize * 8)]uintptr
|
type ObjMask [MaxObjsPerSpan / (goarch.PtrSize * 8)]uintptr
|
||||||
|
|
||||||
// PtrMask is a bitmap where each bit represents a pointer-word in a single runtime page.
|
// PtrMask is a bitmap where each bit represents a pointer-word in a single runtime page.
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func ExpandReference(sizeClass int, packed *gc.ObjMask, unpacked *gc.PtrMask) {
|
||||||
// Look up the size and derive the number of objects in a span.
|
// Look up the size and derive the number of objects in a span.
|
||||||
// We're only concerned with small objects in single-page spans,
|
// We're only concerned with small objects in single-page spans,
|
||||||
// and gc.PtrMask enforces this by being statically sized to
|
// and gc.PtrMask enforces this by being statically sized to
|
||||||
// accomodate only such spans.
|
// accommodate only such spans.
|
||||||
size := uintptr(gc.SizeClassToSize[sizeClass])
|
size := uintptr(gc.SizeClassToSize[sizeClass])
|
||||||
nObj := uintptr(gc.SizeClassToNPages[sizeClass]) * gc.PageSize / size
|
nObj := uintptr(gc.SizeClassToNPages[sizeClass]) * gc.PageSize / size
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ const (
|
||||||
ERROR_NOT_SUPPORTED syscall.Errno = 50
|
ERROR_NOT_SUPPORTED syscall.Errno = 50
|
||||||
ERROR_CALL_NOT_IMPLEMENTED syscall.Errno = 120
|
ERROR_CALL_NOT_IMPLEMENTED syscall.Errno = 120
|
||||||
ERROR_INVALID_NAME syscall.Errno = 123
|
ERROR_INVALID_NAME syscall.Errno = 123
|
||||||
|
ERROR_NEGATIVE_SEEK syscall.Errno = 131
|
||||||
ERROR_LOCK_FAILED syscall.Errno = 167
|
ERROR_LOCK_FAILED syscall.Errno = 167
|
||||||
ERROR_IO_INCOMPLETE syscall.Errno = 996
|
ERROR_IO_INCOMPLETE syscall.Errno = 996
|
||||||
ERROR_NO_TOKEN syscall.Errno = 1008
|
ERROR_NO_TOKEN syscall.Errno = 1008
|
||||||
|
|
@ -195,6 +196,7 @@ const (
|
||||||
//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle
|
//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle
|
||||||
//sys VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) = kernel32.VirtualQuery
|
//sys VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) = kernel32.VirtualQuery
|
||||||
//sys GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) = GetTempPath2W
|
//sys GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) = GetTempPath2W
|
||||||
|
//sys GetFileSizeEx(handle syscall.Handle, size *int64) (err error) = kernel32.GetFileSizeEx
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// flags for CreateToolhelp32Snapshot
|
// flags for CreateToolhelp32Snapshot
|
||||||
|
|
@ -259,7 +261,7 @@ var sendRecvMsgFunc struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WSAMsg struct {
|
type WSAMsg struct {
|
||||||
Name syscall.Pointer
|
Name *syscall.RawSockaddrAny
|
||||||
Namelen int32
|
Namelen int32
|
||||||
Buffers *syscall.WSABuf
|
Buffers *syscall.WSABuf
|
||||||
BufferCount uint32
|
BufferCount uint32
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ var (
|
||||||
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
||||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procGetFileSizeEx = modkernel32.NewProc("GetFileSizeEx")
|
||||||
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
||||||
procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
|
procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
|
||||||
procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
|
procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
|
||||||
|
|
@ -326,6 +327,14 @@ func GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFileSizeEx(handle syscall.Handle, size *int64) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procGetFileSizeEx.Addr(), uintptr(handle), uintptr(unsafe.Pointer(size)))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) {
|
func GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) {
|
||||||
r0, _, e1 := syscall.SyscallN(procGetFinalPathNameByHandleW.Addr(), uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags))
|
r0, _, e1 := syscall.SyscallN(procGetFinalPathNameByHandleW.Addr(), uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags))
|
||||||
n = uint32(r0)
|
n = uint32(r0)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,19 @@
|
||||||
// A file system can be provided by the host operating system
|
// A file system can be provided by the host operating system
|
||||||
// but also by other packages.
|
// but also by other packages.
|
||||||
//
|
//
|
||||||
|
// # Path Names
|
||||||
|
//
|
||||||
|
// The interfaces in this package all operate on the same
|
||||||
|
// path name syntax, regardless of the host operating system.
|
||||||
|
//
|
||||||
|
// Path names are UTF-8-encoded,
|
||||||
|
// unrooted, slash-separated sequences of path elements, like “x/y/z”.
|
||||||
|
// Path names must not contain an element that is “.” or “..” or the empty string,
|
||||||
|
// except for the special case that the name "." may be used for the root directory.
|
||||||
|
// Paths must not start or end with a slash: “/x” and “x/” are invalid.
|
||||||
|
//
|
||||||
|
// # Testing
|
||||||
|
//
|
||||||
// See the [testing/fstest] package for support with testing
|
// See the [testing/fstest] package for support with testing
|
||||||
// implementations of file systems.
|
// implementations of file systems.
|
||||||
package fs
|
package fs
|
||||||
|
|
@ -41,16 +54,13 @@ type FS interface {
|
||||||
// ValidPath reports whether the given path name
|
// ValidPath reports whether the given path name
|
||||||
// is valid for use in a call to Open.
|
// is valid for use in a call to Open.
|
||||||
//
|
//
|
||||||
// Path names passed to open are UTF-8-encoded,
|
|
||||||
// unrooted, slash-separated sequences of path elements, like “x/y/z”.
|
|
||||||
// Path names must not contain an element that is “.” or “..” or the empty string,
|
|
||||||
// except for the special case that the name "." may be used for the root directory.
|
|
||||||
// Paths must not start or end with a slash: “/x” and “x/” are invalid.
|
|
||||||
//
|
|
||||||
// Note that paths are slash-separated on all systems, even Windows.
|
// Note that paths are slash-separated on all systems, even Windows.
|
||||||
// Paths containing other characters such as backslash and colon
|
// Paths containing other characters such as backslash and colon
|
||||||
// are accepted as valid, but those characters must never be
|
// are accepted as valid, but those characters must never be
|
||||||
// interpreted by an [FS] implementation as path element separators.
|
// interpreted by an [FS] implementation as path element separators.
|
||||||
|
// See the [Path Names] section for more details.
|
||||||
|
//
|
||||||
|
// [Path Names]: https://pkg.go.dev/io/fs#hdr-Path_Names
|
||||||
func ValidPath(name string) bool {
|
func ValidPath(name string) bool {
|
||||||
if !utf8.ValidString(name) {
|
if !utf8.ValidString(name) {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -98,24 +98,32 @@ func FormatMediaType(t string, param map[string]string) string {
|
||||||
func checkMediaTypeDisposition(s string) error {
|
func checkMediaTypeDisposition(s string) error {
|
||||||
typ, rest := consumeToken(s)
|
typ, rest := consumeToken(s)
|
||||||
if typ == "" {
|
if typ == "" {
|
||||||
return errors.New("mime: no media type")
|
return errNoMediaType
|
||||||
}
|
}
|
||||||
if rest == "" {
|
if rest == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(rest, "/") {
|
var ok bool
|
||||||
return errors.New("mime: expected slash after first token")
|
if rest, ok = strings.CutPrefix(rest, "/"); !ok {
|
||||||
|
return errNoSlashAfterFirstToken
|
||||||
}
|
}
|
||||||
subtype, rest := consumeToken(rest[1:])
|
subtype, rest := consumeToken(rest)
|
||||||
if subtype == "" {
|
if subtype == "" {
|
||||||
return errors.New("mime: expected token after slash")
|
return errNoTokenAfterSlash
|
||||||
}
|
}
|
||||||
if rest != "" {
|
if rest != "" {
|
||||||
return errors.New("mime: unexpected content after media subtype")
|
return errUnexpectedContentAfterMediaSubtype
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoMediaType = errors.New("mime: no media type")
|
||||||
|
errNoSlashAfterFirstToken = errors.New("mime: expected slash after first token")
|
||||||
|
errNoTokenAfterSlash = errors.New("mime: expected token after slash")
|
||||||
|
errUnexpectedContentAfterMediaSubtype = errors.New("mime: unexpected content after media subtype")
|
||||||
|
)
|
||||||
|
|
||||||
// ErrInvalidMediaParameter is returned by [ParseMediaType] if
|
// ErrInvalidMediaParameter is returned by [ParseMediaType] if
|
||||||
// the media type value was found but there was an error parsing
|
// the media type value was found but there was an error parsing
|
||||||
// the optional parameters
|
// the optional parameters
|
||||||
|
|
@ -169,7 +177,6 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
|
||||||
if continuation == nil {
|
if continuation == nil {
|
||||||
continuation = make(map[string]map[string]string)
|
continuation = make(map[string]map[string]string)
|
||||||
}
|
}
|
||||||
var ok bool
|
|
||||||
if pmap, ok = continuation[baseName]; !ok {
|
if pmap, ok = continuation[baseName]; !ok {
|
||||||
continuation[baseName] = make(map[string]string)
|
continuation[baseName] = make(map[string]string)
|
||||||
pmap = continuation[baseName]
|
pmap = continuation[baseName]
|
||||||
|
|
@ -177,7 +184,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
|
||||||
}
|
}
|
||||||
if v, exists := pmap[key]; exists && v != value {
|
if v, exists := pmap[key]; exists && v != value {
|
||||||
// Duplicate parameter names are incorrect, but we allow them if they are equal.
|
// Duplicate parameter names are incorrect, but we allow them if they are equal.
|
||||||
return "", nil, errors.New("mime: duplicate parameter name")
|
return "", nil, errDuplicateParamName
|
||||||
}
|
}
|
||||||
pmap[key] = value
|
pmap[key] = value
|
||||||
v = rest
|
v = rest
|
||||||
|
|
@ -227,27 +234,28 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errDuplicateParamName = errors.New("mime: duplicate parameter name")
|
||||||
|
|
||||||
func decode2231Enc(v string) (string, bool) {
|
func decode2231Enc(v string) (string, bool) {
|
||||||
sv := strings.SplitN(v, "'", 3)
|
charset, v, ok := strings.Cut(v, "'")
|
||||||
if len(sv) != 3 {
|
if !ok {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
// TODO: ignoring lang in sv[1] for now. If anybody needs it we'll
|
// TODO: ignoring the language part for now. If anybody needs it, we'll
|
||||||
// need to decide how to expose it in the API. But I'm not sure
|
// need to decide how to expose it in the API. But I'm not sure
|
||||||
// anybody uses it in practice.
|
// anybody uses it in practice.
|
||||||
charset := strings.ToLower(sv[0])
|
_, extOtherVals, ok := strings.Cut(v, "'")
|
||||||
if len(charset) == 0 {
|
if !ok {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
if charset != "us-ascii" && charset != "utf-8" {
|
charset = strings.ToLower(charset)
|
||||||
// TODO: unsupported encoding
|
switch charset {
|
||||||
|
case "us-ascii", "utf-8":
|
||||||
|
default:
|
||||||
|
// Empty or unsupported encoding.
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
encv, err := percentHexUnescape(sv[2])
|
return percentHexUnescape(extOtherVals)
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return encv, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// consumeToken consumes a token from the beginning of provided
|
// consumeToken consumes a token from the beginning of provided
|
||||||
|
|
@ -309,11 +317,11 @@ func consumeValue(v string) (value, rest string) {
|
||||||
|
|
||||||
func consumeMediaParam(v string) (param, value, rest string) {
|
func consumeMediaParam(v string) (param, value, rest string) {
|
||||||
rest = strings.TrimLeftFunc(v, unicode.IsSpace)
|
rest = strings.TrimLeftFunc(v, unicode.IsSpace)
|
||||||
if !strings.HasPrefix(rest, ";") {
|
var ok bool
|
||||||
|
if rest, ok = strings.CutPrefix(rest, ";"); !ok {
|
||||||
return "", "", v
|
return "", "", v
|
||||||
}
|
}
|
||||||
|
|
||||||
rest = rest[1:] // consume semicolon
|
|
||||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||||
param, rest = consumeToken(rest)
|
param, rest = consumeToken(rest)
|
||||||
param = strings.ToLower(param)
|
param = strings.ToLower(param)
|
||||||
|
|
@ -322,10 +330,9 @@ func consumeMediaParam(v string) (param, value, rest string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||||
if !strings.HasPrefix(rest, "=") {
|
if rest, ok = strings.CutPrefix(rest, "="); !ok {
|
||||||
return "", "", v
|
return "", "", v
|
||||||
}
|
}
|
||||||
rest = rest[1:] // consume equals sign
|
|
||||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||||
value, rest2 := consumeValue(rest)
|
value, rest2 := consumeValue(rest)
|
||||||
if value == "" && rest2 == rest {
|
if value == "" && rest2 == rest {
|
||||||
|
|
@ -335,7 +342,7 @@ func consumeMediaParam(v string) (param, value, rest string) {
|
||||||
return param, value, rest
|
return param, value, rest
|
||||||
}
|
}
|
||||||
|
|
||||||
func percentHexUnescape(s string) (string, error) {
|
func percentHexUnescape(s string) (string, bool) {
|
||||||
// Count %, check that they're well-formed.
|
// Count %, check that they're well-formed.
|
||||||
percents := 0
|
percents := 0
|
||||||
for i := 0; i < len(s); {
|
for i := 0; i < len(s); {
|
||||||
|
|
@ -345,16 +352,12 @@ func percentHexUnescape(s string) (string, error) {
|
||||||
}
|
}
|
||||||
percents++
|
percents++
|
||||||
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
||||||
s = s[i:]
|
return "", false
|
||||||
if len(s) > 3 {
|
|
||||||
s = s[0:3]
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("mime: bogus characters after %%: %q", s)
|
|
||||||
}
|
}
|
||||||
i += 3
|
i += 3
|
||||||
}
|
}
|
||||||
if percents == 0 {
|
if percents == 0 {
|
||||||
return s, nil
|
return s, true
|
||||||
}
|
}
|
||||||
|
|
||||||
t := make([]byte, len(s)-2*percents)
|
t := make([]byte, len(s)-2*percents)
|
||||||
|
|
@ -371,7 +374,7 @@ func percentHexUnescape(s string) (string, error) {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string(t), nil
|
return string(t), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func ishex(c byte) bool {
|
func ishex(c byte) bool {
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,20 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"", "\r", "%0D", "\n", "%0A")
|
||||||
|
|
||||||
|
// escapeQuotes escapes special characters in field parameter values.
|
||||||
|
//
|
||||||
|
// For historical reasons, this uses \ escaping for " and \ characters,
|
||||||
|
// and percent encoding for CR and LF.
|
||||||
|
//
|
||||||
|
// The WhatWG specification for form data encoding suggests that we should
|
||||||
|
// use percent encoding for " (%22), and should not escape \.
|
||||||
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart/form-data-encoding-algorithm
|
||||||
|
//
|
||||||
|
// Empirically, as of the time this comment was written, it is necessary
|
||||||
|
// to escape \ characters or else Chrome (and possibly other browsers) will
|
||||||
|
// interpet the unescaped \ as an escape.
|
||||||
func escapeQuotes(s string) string {
|
func escapeQuotes(s string) string {
|
||||||
return quoteEscaper.Replace(s)
|
return quoteEscaper.Replace(s)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue