@@ -907,6 +907,7 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL | |||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= | golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= | ||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= | |||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | ||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
@@ -927,6 +928,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ | |||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= | ||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= | |||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
@@ -971,6 +973,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | ||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= | |||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
@@ -97,6 +97,7 @@ package module | |||||
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"path" | |||||
"sort" | "sort" | ||||
"strings" | "strings" | ||||
"unicode" | "unicode" | ||||
@@ -223,14 +224,18 @@ func firstPathOK(r rune) bool { | |||||
'a' <= r && r <= 'z' | 'a' <= r && r <= 'z' | ||||
} | } | ||||
// pathOK reports whether r can appear in an import path element. | |||||
// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. | |||||
// This matches what "go get" has historically recognized in import paths. | |||||
// modPathOK reports whether r can appear in a module path element. | |||||
// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~. | |||||
// | |||||
// This matches what "go get" has historically recognized in import paths, | |||||
// and avoids confusing sequences like '%20' or '+' that would change meaning | |||||
// if used in a URL. | |||||
// | |||||
// TODO(rsc): We would like to allow Unicode letters, but that requires additional | // TODO(rsc): We would like to allow Unicode letters, but that requires additional | ||||
// care in the safe encoding (see "escaped paths" above). | // care in the safe encoding (see "escaped paths" above). | ||||
func pathOK(r rune) bool { | |||||
func modPathOK(r rune) bool { | |||||
if r < utf8.RuneSelf { | if r < utf8.RuneSelf { | ||||
return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || | |||||
return r == '-' || r == '.' || r == '_' || r == '~' || | |||||
'0' <= r && r <= '9' || | '0' <= r && r <= '9' || | ||||
'A' <= r && r <= 'Z' || | 'A' <= r && r <= 'Z' || | ||||
'a' <= r && r <= 'z' | 'a' <= r && r <= 'z' | ||||
@@ -238,6 +243,17 @@ func pathOK(r rune) bool { | |||||
return false | return false | ||||
} | } | ||||
// modPathOK reports whether r can appear in a package import path element. | |||||
// | |||||
// Import paths are intermediate between module paths and file paths: we allow | |||||
// disallow characters that would be confusing or ambiguous as arguments to | |||||
// 'go get' (such as '@' and ' ' ), but allow certain characters that are | |||||
// otherwise-unambiguous on the command line and historically used for some | |||||
// binary names (such as '++' as a suffix for compiler binaries and wrappers). | |||||
func importPathOK(r rune) bool { | |||||
return modPathOK(r) || r == '+' | |||||
} | |||||
// fileNameOK reports whether r can appear in a file name. | // fileNameOK reports whether r can appear in a file name. | ||||
// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. | // For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. | ||||
// If we expand the set of allowed characters here, we have to | // If we expand the set of allowed characters here, we have to | ||||
@@ -269,7 +285,7 @@ func fileNameOK(r rune) bool { | |||||
// CheckPath checks that a module path is valid. | // CheckPath checks that a module path is valid. | ||||
// A valid module path is a valid import path, as checked by CheckImportPath, | // A valid module path is a valid import path, as checked by CheckImportPath, | ||||
// with two additional constraints. | |||||
// with three additional constraints. | |||||
// First, the leading path element (up to the first slash, if any), | // First, the leading path element (up to the first slash, if any), | ||||
// by convention a domain name, must contain only lower-case ASCII letters, | // by convention a domain name, must contain only lower-case ASCII letters, | ||||
// ASCII digits, dots (U+002E), and dashes (U+002D); | // ASCII digits, dots (U+002E), and dashes (U+002D); | ||||
@@ -279,8 +295,9 @@ func fileNameOK(r rune) bool { | |||||
// and must not contain any dots. For paths beginning with "gopkg.in/", | // and must not contain any dots. For paths beginning with "gopkg.in/", | ||||
// this second requirement is replaced by a requirement that the path | // this second requirement is replaced by a requirement that the path | ||||
// follow the gopkg.in server's conventions. | // follow the gopkg.in server's conventions. | ||||
// Third, no path element may begin with a dot. | |||||
func CheckPath(path string) error { | func CheckPath(path string) error { | ||||
if err := checkPath(path, false); err != nil { | |||||
if err := checkPath(path, modulePath); err != nil { | |||||
return fmt.Errorf("malformed module path %q: %v", path, err) | return fmt.Errorf("malformed module path %q: %v", path, err) | ||||
} | } | ||||
i := strings.Index(path, "/") | i := strings.Index(path, "/") | ||||
@@ -313,29 +330,41 @@ func CheckPath(path string) error { | |||||
// separated by slashes (U+002F). (It must not begin with nor end in a slash.) | // separated by slashes (U+002F). (It must not begin with nor end in a slash.) | ||||
// | // | ||||
// A valid path element is a non-empty string made up of | // A valid path element is a non-empty string made up of | ||||
// ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. | |||||
// It must not begin or end with a dot (U+002E), nor contain two dots in a row. | |||||
// ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~. | |||||
// It must not end with a dot (U+002E), nor contain two dots in a row. | |||||
// | // | ||||
// The element prefix up to the first dot must not be a reserved file name | // The element prefix up to the first dot must not be a reserved file name | ||||
// on Windows, regardless of case (CON, com1, NuL, and so on). | |||||
// on Windows, regardless of case (CON, com1, NuL, and so on). The element | |||||
// must not have a suffix of a tilde followed by one or more ASCII digits | |||||
// (to exclude paths elements that look like Windows short-names). | |||||
// | // | ||||
// CheckImportPath may be less restrictive in the future, but see the | // CheckImportPath may be less restrictive in the future, but see the | ||||
// top-level package documentation for additional information about | // top-level package documentation for additional information about | ||||
// subtleties of Unicode. | // subtleties of Unicode. | ||||
func CheckImportPath(path string) error { | func CheckImportPath(path string) error { | ||||
if err := checkPath(path, false); err != nil { | |||||
if err := checkPath(path, importPath); err != nil { | |||||
return fmt.Errorf("malformed import path %q: %v", path, err) | return fmt.Errorf("malformed import path %q: %v", path, err) | ||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
// pathKind indicates what kind of path we're checking. Module paths, | |||||
// import paths, and file paths have different restrictions. | |||||
type pathKind int | |||||
const ( | |||||
modulePath pathKind = iota | |||||
importPath | |||||
filePath | |||||
) | |||||
// checkPath checks that a general path is valid. | // checkPath checks that a general path is valid. | ||||
// It returns an error describing why but not mentioning path. | // It returns an error describing why but not mentioning path. | ||||
// Because these checks apply to both module paths and import paths, | // Because these checks apply to both module paths and import paths, | ||||
// the caller is expected to add the "malformed ___ path %q: " prefix. | // the caller is expected to add the "malformed ___ path %q: " prefix. | ||||
// fileName indicates whether the final element of the path is a file name | // fileName indicates whether the final element of the path is a file name | ||||
// (as opposed to a directory name). | // (as opposed to a directory name). | ||||
func checkPath(path string, fileName bool) error { | |||||
func checkPath(path string, kind pathKind) error { | |||||
if !utf8.ValidString(path) { | if !utf8.ValidString(path) { | ||||
return fmt.Errorf("invalid UTF-8") | return fmt.Errorf("invalid UTF-8") | ||||
} | } | ||||
@@ -354,39 +383,45 @@ func checkPath(path string, fileName bool) error { | |||||
elemStart := 0 | elemStart := 0 | ||||
for i, r := range path { | for i, r := range path { | ||||
if r == '/' { | if r == '/' { | ||||
if err := checkElem(path[elemStart:i], fileName); err != nil { | |||||
if err := checkElem(path[elemStart:i], kind); err != nil { | |||||
return err | return err | ||||
} | } | ||||
elemStart = i + 1 | elemStart = i + 1 | ||||
} | } | ||||
} | } | ||||
if err := checkElem(path[elemStart:], fileName); err != nil { | |||||
if err := checkElem(path[elemStart:], kind); err != nil { | |||||
return err | return err | ||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
// checkElem checks whether an individual path element is valid. | // checkElem checks whether an individual path element is valid. | ||||
// fileName indicates whether the element is a file name (not a directory name). | |||||
func checkElem(elem string, fileName bool) error { | |||||
func checkElem(elem string, kind pathKind) error { | |||||
if elem == "" { | if elem == "" { | ||||
return fmt.Errorf("empty path element") | return fmt.Errorf("empty path element") | ||||
} | } | ||||
if strings.Count(elem, ".") == len(elem) { | if strings.Count(elem, ".") == len(elem) { | ||||
return fmt.Errorf("invalid path element %q", elem) | return fmt.Errorf("invalid path element %q", elem) | ||||
} | } | ||||
if elem[0] == '.' && !fileName { | |||||
if elem[0] == '.' && kind == modulePath { | |||||
return fmt.Errorf("leading dot in path element") | return fmt.Errorf("leading dot in path element") | ||||
} | } | ||||
if elem[len(elem)-1] == '.' { | if elem[len(elem)-1] == '.' { | ||||
return fmt.Errorf("trailing dot in path element") | return fmt.Errorf("trailing dot in path element") | ||||
} | } | ||||
charOK := pathOK | |||||
if fileName { | |||||
charOK = fileNameOK | |||||
} | |||||
for _, r := range elem { | for _, r := range elem { | ||||
if !charOK(r) { | |||||
ok := false | |||||
switch kind { | |||||
case modulePath: | |||||
ok = modPathOK(r) | |||||
case importPath: | |||||
ok = importPathOK(r) | |||||
case filePath: | |||||
ok = fileNameOK(r) | |||||
default: | |||||
panic(fmt.Sprintf("internal error: invalid kind %v", kind)) | |||||
} | |||||
if !ok { | |||||
return fmt.Errorf("invalid char %q", r) | return fmt.Errorf("invalid char %q", r) | ||||
} | } | ||||
} | } | ||||
@@ -402,6 +437,29 @@ func checkElem(elem string, fileName bool) error { | |||||
return fmt.Errorf("%q disallowed as path element component on Windows", short) | return fmt.Errorf("%q disallowed as path element component on Windows", short) | ||||
} | } | ||||
} | } | ||||
if kind == filePath { | |||||
// don't check for Windows short-names in file names. They're | |||||
// only an issue for import paths. | |||||
return nil | |||||
} | |||||
// Reject path components that look like Windows short-names. | |||||
// Those usually end in a tilde followed by one or more ASCII digits. | |||||
if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 { | |||||
suffix := short[tilde+1:] | |||||
suffixIsDigits := true | |||||
for _, r := range suffix { | |||||
if r < '0' || r > '9' { | |||||
suffixIsDigits = false | |||||
break | |||||
} | |||||
} | |||||
if suffixIsDigits { | |||||
return fmt.Errorf("trailing tilde and digits in path element") | |||||
} | |||||
} | |||||
return nil | return nil | ||||
} | } | ||||
@@ -418,7 +476,7 @@ func checkElem(elem string, fileName bool) error { | |||||
// top-level package documentation for additional information about | // top-level package documentation for additional information about | ||||
// subtleties of Unicode. | // subtleties of Unicode. | ||||
func CheckFilePath(path string) error { | func CheckFilePath(path string) error { | ||||
if err := checkPath(path, true); err != nil { | |||||
if err := checkPath(path, filePath); err != nil { | |||||
return fmt.Errorf("malformed file path %q: %v", path, err) | return fmt.Errorf("malformed file path %q: %v", path, err) | ||||
} | } | ||||
return nil | return nil | ||||
@@ -621,7 +679,7 @@ func EscapePath(path string) (escaped string, err error) { | |||||
// Versions are allowed to be in non-semver form but must be valid file names | // Versions are allowed to be in non-semver form but must be valid file names | ||||
// and not contain exclamation marks. | // and not contain exclamation marks. | ||||
func EscapeVersion(v string) (escaped string, err error) { | func EscapeVersion(v string) (escaped string, err error) { | ||||
if err := checkElem(v, true); err != nil || strings.Contains(v, "!") { | |||||
if err := checkElem(v, filePath); err != nil || strings.Contains(v, "!") { | |||||
return "", &InvalidVersionError{ | return "", &InvalidVersionError{ | ||||
Version: v, | Version: v, | ||||
Err: fmt.Errorf("disallowed version string"), | Err: fmt.Errorf("disallowed version string"), | ||||
@@ -680,7 +738,7 @@ func UnescapeVersion(escaped string) (v string, err error) { | |||||
if !ok { | if !ok { | ||||
return "", fmt.Errorf("invalid escaped version %q", escaped) | return "", fmt.Errorf("invalid escaped version %q", escaped) | ||||
} | } | ||||
if err := checkElem(v, true); err != nil { | |||||
if err := checkElem(v, filePath); err != nil { | |||||
return "", fmt.Errorf("invalid escaped version %q: %v", v, err) | return "", fmt.Errorf("invalid escaped version %q: %v", v, err) | ||||
} | } | ||||
return v, nil | return v, nil | ||||
@@ -716,3 +774,49 @@ func unescapeString(escaped string) (string, bool) { | |||||
} | } | ||||
return string(buf), true | return string(buf), true | ||||
} | } | ||||
// MatchPrefixPatterns reports whether any path prefix of target matches one of | |||||
// the glob patterns (as defined by path.Match) in the comma-separated globs | |||||
// list. This implements the algorithm used when matching a module path to the | |||||
// GOPRIVATE environment variable, as described by 'go help module-private'. | |||||
// | |||||
// It ignores any empty or malformed patterns in the list. | |||||
func MatchPrefixPatterns(globs, target string) bool { | |||||
for globs != "" { | |||||
// Extract next non-empty glob in comma-separated list. | |||||
var glob string | |||||
if i := strings.Index(globs, ","); i >= 0 { | |||||
glob, globs = globs[:i], globs[i+1:] | |||||
} else { | |||||
glob, globs = globs, "" | |||||
} | |||||
if glob == "" { | |||||
continue | |||||
} | |||||
// A glob with N+1 path elements (N slashes) needs to be matched | |||||
// against the first N+1 path elements of target, | |||||
// which end just before the N+1'th slash. | |||||
n := strings.Count(glob, "/") | |||||
prefix := target | |||||
// Walk target, counting slashes, truncating at the N+1'th slash. | |||||
for i := 0; i < len(target); i++ { | |||||
if target[i] == '/' { | |||||
if n == 0 { | |||||
prefix = target[:i] | |||||
break | |||||
} | |||||
n-- | |||||
} | |||||
} | |||||
if n > 0 { | |||||
// Not enough prefix elements. | |||||
continue | |||||
} | |||||
matched, _ := path.Match(glob, prefix) | |||||
if matched { | |||||
return true | |||||
} | |||||
} | |||||
return false | |||||
} |
@@ -138,6 +138,9 @@ func Compare(v, w string) int { | |||||
// Max canonicalizes its arguments and then returns the version string | // Max canonicalizes its arguments and then returns the version string | ||||
// that compares greater. | // that compares greater. | ||||
// | |||||
// Deprecated: use Compare instead. In most cases, returning a canonicalized | |||||
// version is not expected or desired. | |||||
func Max(v, w string) string { | func Max(v, w string) string { | ||||
v = Canonical(v) | v = Canonical(v) | ||||
w = Canonical(w) | w = Canonical(w) | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build go1.7 | |||||
// +build go1.7 | // +build go1.7 | ||||
package context | package context | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build go1.9 | |||||
// +build go1.9 | // +build go1.9 | ||||
package context | package context | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build !go1.7 | |||||
// +build !go1.7 | // +build !go1.7 | ||||
package context | package context | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build !go1.9 | |||||
// +build !go1.9 | // +build !go1.9 | ||||
package context | package context | ||||
@@ -52,7 +52,7 @@ var isSpecialElementMap = map[string]bool{ | |||||
"iframe": true, | "iframe": true, | ||||
"img": true, | "img": true, | ||||
"input": true, | "input": true, | ||||
"keygen": true, | |||||
"keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. | |||||
"li": true, | "li": true, | ||||
"link": true, | "link": true, | ||||
"listing": true, | "listing": true, | ||||
@@ -161,65 +161,62 @@ var mathMLAttributeAdjustments = map[string]string{ | |||||
} | } | ||||
var svgAttributeAdjustments = map[string]string{ | var svgAttributeAdjustments = map[string]string{ | ||||
"attributename": "attributeName", | |||||
"attributetype": "attributeType", | |||||
"basefrequency": "baseFrequency", | |||||
"baseprofile": "baseProfile", | |||||
"calcmode": "calcMode", | |||||
"clippathunits": "clipPathUnits", | |||||
"contentscripttype": "contentScriptType", | |||||
"contentstyletype": "contentStyleType", | |||||
"diffuseconstant": "diffuseConstant", | |||||
"edgemode": "edgeMode", | |||||
"externalresourcesrequired": "externalResourcesRequired", | |||||
"filterunits": "filterUnits", | |||||
"glyphref": "glyphRef", | |||||
"gradienttransform": "gradientTransform", | |||||
"gradientunits": "gradientUnits", | |||||
"kernelmatrix": "kernelMatrix", | |||||
"kernelunitlength": "kernelUnitLength", | |||||
"keypoints": "keyPoints", | |||||
"keysplines": "keySplines", | |||||
"keytimes": "keyTimes", | |||||
"lengthadjust": "lengthAdjust", | |||||
"limitingconeangle": "limitingConeAngle", | |||||
"markerheight": "markerHeight", | |||||
"markerunits": "markerUnits", | |||||
"markerwidth": "markerWidth", | |||||
"maskcontentunits": "maskContentUnits", | |||||
"maskunits": "maskUnits", | |||||
"numoctaves": "numOctaves", | |||||
"pathlength": "pathLength", | |||||
"patterncontentunits": "patternContentUnits", | |||||
"patterntransform": "patternTransform", | |||||
"patternunits": "patternUnits", | |||||
"pointsatx": "pointsAtX", | |||||
"pointsaty": "pointsAtY", | |||||
"pointsatz": "pointsAtZ", | |||||
"preservealpha": "preserveAlpha", | |||||
"preserveaspectratio": "preserveAspectRatio", | |||||
"primitiveunits": "primitiveUnits", | |||||
"refx": "refX", | |||||
"refy": "refY", | |||||
"repeatcount": "repeatCount", | |||||
"repeatdur": "repeatDur", | |||||
"requiredextensions": "requiredExtensions", | |||||
"requiredfeatures": "requiredFeatures", | |||||
"specularconstant": "specularConstant", | |||||
"specularexponent": "specularExponent", | |||||
"spreadmethod": "spreadMethod", | |||||
"startoffset": "startOffset", | |||||
"stddeviation": "stdDeviation", | |||||
"stitchtiles": "stitchTiles", | |||||
"surfacescale": "surfaceScale", | |||||
"systemlanguage": "systemLanguage", | |||||
"tablevalues": "tableValues", | |||||
"targetx": "targetX", | |||||
"targety": "targetY", | |||||
"textlength": "textLength", | |||||
"viewbox": "viewBox", | |||||
"viewtarget": "viewTarget", | |||||
"xchannelselector": "xChannelSelector", | |||||
"ychannelselector": "yChannelSelector", | |||||
"zoomandpan": "zoomAndPan", | |||||
"attributename": "attributeName", | |||||
"attributetype": "attributeType", | |||||
"basefrequency": "baseFrequency", | |||||
"baseprofile": "baseProfile", | |||||
"calcmode": "calcMode", | |||||
"clippathunits": "clipPathUnits", | |||||
"diffuseconstant": "diffuseConstant", | |||||
"edgemode": "edgeMode", | |||||
"filterunits": "filterUnits", | |||||
"glyphref": "glyphRef", | |||||
"gradienttransform": "gradientTransform", | |||||
"gradientunits": "gradientUnits", | |||||
"kernelmatrix": "kernelMatrix", | |||||
"kernelunitlength": "kernelUnitLength", | |||||
"keypoints": "keyPoints", | |||||
"keysplines": "keySplines", | |||||
"keytimes": "keyTimes", | |||||
"lengthadjust": "lengthAdjust", | |||||
"limitingconeangle": "limitingConeAngle", | |||||
"markerheight": "markerHeight", | |||||
"markerunits": "markerUnits", | |||||
"markerwidth": "markerWidth", | |||||
"maskcontentunits": "maskContentUnits", | |||||
"maskunits": "maskUnits", | |||||
"numoctaves": "numOctaves", | |||||
"pathlength": "pathLength", | |||||
"patterncontentunits": "patternContentUnits", | |||||
"patterntransform": "patternTransform", | |||||
"patternunits": "patternUnits", | |||||
"pointsatx": "pointsAtX", | |||||
"pointsaty": "pointsAtY", | |||||
"pointsatz": "pointsAtZ", | |||||
"preservealpha": "preserveAlpha", | |||||
"preserveaspectratio": "preserveAspectRatio", | |||||
"primitiveunits": "primitiveUnits", | |||||
"refx": "refX", | |||||
"refy": "refY", | |||||
"repeatcount": "repeatCount", | |||||
"repeatdur": "repeatDur", | |||||
"requiredextensions": "requiredExtensions", | |||||
"requiredfeatures": "requiredFeatures", | |||||
"specularconstant": "specularConstant", | |||||
"specularexponent": "specularExponent", | |||||
"spreadmethod": "spreadMethod", | |||||
"startoffset": "startOffset", | |||||
"stddeviation": "stdDeviation", | |||||
"stitchtiles": "stitchTiles", | |||||
"surfacescale": "surfaceScale", | |||||
"systemlanguage": "systemLanguage", | |||||
"tablevalues": "tableValues", | |||||
"targetx": "targetX", | |||||
"targety": "targetY", | |||||
"textlength": "textLength", | |||||
"viewbox": "viewBox", | |||||
"viewtarget": "viewTarget", | |||||
"xchannelselector": "xChannelSelector", | |||||
"ychannelselector": "yChannelSelector", | |||||
"zoomandpan": "zoomAndPan", | |||||
} | } |
@@ -728,7 +728,13 @@ func inHeadNoscriptIM(p *parser) bool { | |||||
return inBodyIM(p) | return inBodyIM(p) | ||||
case a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Style: | case a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Style: | ||||
return inHeadIM(p) | return inHeadIM(p) | ||||
case a.Head, a.Noscript: | |||||
case a.Head: | |||||
// Ignore the token. | |||||
return true | |||||
case a.Noscript: | |||||
// Don't let the tokenizer go into raw text mode even when a <noscript> | |||||
// tag is in "in head noscript" insertion mode. | |||||
p.tokenizer.NextIsNotRawText() | |||||
// Ignore the token. | // Ignore the token. | ||||
return true | return true | ||||
} | } | ||||
@@ -1790,6 +1796,13 @@ func inSelectIM(p *parser) bool { | |||||
return true | return true | ||||
case a.Script, a.Template: | case a.Script, a.Template: | ||||
return inHeadIM(p) | return inHeadIM(p) | ||||
case a.Iframe, a.Noembed, a.Noframes, a.Noscript, a.Plaintext, a.Style, a.Title, a.Xmp: | |||||
// Don't let the tokenizer go into raw text mode when there are raw tags | |||||
// to be ignored. These tags should be ignored from the tokenizer | |||||
// properly. | |||||
p.tokenizer.NextIsNotRawText() | |||||
// Ignore the token. | |||||
return true | |||||
} | } | ||||
case EndTagToken: | case EndTagToken: | ||||
switch p.tok.DataAtom { | switch p.tok.DataAtom { | ||||
@@ -263,7 +263,7 @@ var voidElements = map[string]bool{ | |||||
"hr": true, | "hr": true, | ||||
"img": true, | "img": true, | ||||
"input": true, | "input": true, | ||||
"keygen": true, | |||||
"keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. | |||||
"link": true, | "link": true, | ||||
"meta": true, | "meta": true, | ||||
"param": true, | "param": true, | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build go1.11 | |||||
// +build go1.11 | // +build go1.11 | ||||
package http2 | package http2 | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build !go1.11 | |||||
// +build !go1.11 | // +build !go1.11 | ||||
package http2 | package http2 | ||||
@@ -1293,7 +1293,9 @@ func (sc *serverConn) startGracefulShutdown() { | |||||
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) }) | sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) }) | ||||
} | } | ||||
// After sending GOAWAY, the connection will close after goAwayTimeout. | |||||
// After sending GOAWAY with an error code (non-graceful shutdown), the | |||||
// connection will close after goAwayTimeout. | |||||
// | |||||
// If we close the connection immediately after sending GOAWAY, there may | // If we close the connection immediately after sending GOAWAY, there may | ||||
// be unsent data in our kernel receive buffer, which will cause the kernel | // be unsent data in our kernel receive buffer, which will cause the kernel | ||||
// to send a TCP RST on close() instead of a FIN. This RST will abort the | // to send a TCP RST on close() instead of a FIN. This RST will abort the | ||||
@@ -1629,23 +1631,37 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error { | |||||
func (sc *serverConn) processData(f *DataFrame) error { | func (sc *serverConn) processData(f *DataFrame) error { | ||||
sc.serveG.check() | sc.serveG.check() | ||||
if sc.inGoAway && sc.goAwayCode != ErrCodeNo { | |||||
id := f.Header().StreamID | |||||
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) { | |||||
// Discard all DATA frames if the GOAWAY is due to an | |||||
// error, or: | |||||
// | |||||
// Section 6.8: After sending a GOAWAY frame, the sender | |||||
// can discard frames for streams initiated by the | |||||
// receiver with identifiers higher than the identified | |||||
// last stream. | |||||
return nil | return nil | ||||
} | } | ||||
data := f.Data() | |||||
// "If a DATA frame is received whose stream is not in "open" | |||||
// or "half closed (local)" state, the recipient MUST respond | |||||
// with a stream error (Section 5.4.2) of type STREAM_CLOSED." | |||||
id := f.Header().StreamID | |||||
data := f.Data() | |||||
state, st := sc.state(id) | state, st := sc.state(id) | ||||
if id == 0 || state == stateIdle { | if id == 0 || state == stateIdle { | ||||
// Section 6.1: "DATA frames MUST be associated with a | |||||
// stream. If a DATA frame is received whose stream | |||||
// identifier field is 0x0, the recipient MUST respond | |||||
// with a connection error (Section 5.4.1) of type | |||||
// PROTOCOL_ERROR." | |||||
// | |||||
// Section 5.1: "Receiving any frame other than HEADERS | // Section 5.1: "Receiving any frame other than HEADERS | ||||
// or PRIORITY on a stream in this state MUST be | // or PRIORITY on a stream in this state MUST be | ||||
// treated as a connection error (Section 5.4.1) of | // treated as a connection error (Section 5.4.1) of | ||||
// type PROTOCOL_ERROR." | // type PROTOCOL_ERROR." | ||||
return ConnectionError(ErrCodeProtocol) | return ConnectionError(ErrCodeProtocol) | ||||
} | } | ||||
// "If a DATA frame is received whose stream is not in "open" | |||||
// or "half closed (local)" state, the recipient MUST respond | |||||
// with a stream error (Section 5.4.2) of type STREAM_CLOSED." | |||||
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued { | if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued { | ||||
// This includes sending a RST_STREAM if the stream is | // This includes sending a RST_STREAM if the stream is | ||||
// in stateHalfClosedLocal (which currently means that | // in stateHalfClosedLocal (which currently means that | ||||
@@ -1694,6 +1710,7 @@ func (sc *serverConn) processData(f *DataFrame) error { | |||||
if len(data) > 0 { | if len(data) > 0 { | ||||
wrote, err := st.body.Write(data) | wrote, err := st.body.Write(data) | ||||
if err != nil { | if err != nil { | ||||
sc.sendWindowUpdate(nil, int(f.Length)-wrote) | |||||
return streamError(id, ErrCodeStreamClosed) | return streamError(id, ErrCodeStreamClosed) | ||||
} | } | ||||
if wrote != len(data) { | if wrote != len(data) { | ||||
@@ -2020,7 +2037,11 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res | |||||
} | } | ||||
if bodyOpen { | if bodyOpen { | ||||
if vv, ok := rp.header["Content-Length"]; ok { | if vv, ok := rp.header["Content-Length"]; ok { | ||||
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64) | |||||
if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil { | |||||
req.ContentLength = int64(cl) | |||||
} else { | |||||
req.ContentLength = 0 | |||||
} | |||||
} else { | } else { | ||||
req.ContentLength = -1 | req.ContentLength = -1 | ||||
} | } | ||||
@@ -2403,9 +2424,8 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { | |||||
var ctype, clen string | var ctype, clen string | ||||
if clen = rws.snapHeader.Get("Content-Length"); clen != "" { | if clen = rws.snapHeader.Get("Content-Length"); clen != "" { | ||||
rws.snapHeader.Del("Content-Length") | rws.snapHeader.Del("Content-Length") | ||||
clen64, err := strconv.ParseInt(clen, 10, 64) | |||||
if err == nil && clen64 >= 0 { | |||||
rws.sentContentLen = clen64 | |||||
if cl, err := strconv.ParseUint(clen, 10, 63); err == nil { | |||||
rws.sentContentLen = int64(cl) | |||||
} else { | } else { | ||||
clen = "" | clen = "" | ||||
} | } | ||||
@@ -108,6 +108,19 @@ type Transport struct { | |||||
// waiting for their turn. | // waiting for their turn. | ||||
StrictMaxConcurrentStreams bool | StrictMaxConcurrentStreams bool | ||||
// ReadIdleTimeout is the timeout after which a health check using ping | |||||
// frame will be carried out if no frame is received on the connection. | |||||
// Note that a ping response will is considered a received frame, so if | |||||
// there is no other traffic on the connection, the health check will | |||||
// be performed every ReadIdleTimeout interval. | |||||
// If zero, no health check is performed. | |||||
ReadIdleTimeout time.Duration | |||||
// PingTimeout is the timeout after which the connection will be closed | |||||
// if a response to Ping is not received. | |||||
// Defaults to 15s. | |||||
PingTimeout time.Duration | |||||
// t1, if non-nil, is the standard library Transport using | // t1, if non-nil, is the standard library Transport using | ||||
// this transport. Its settings are used (but not its | // this transport. Its settings are used (but not its | ||||
// RoundTrip method, etc). | // RoundTrip method, etc). | ||||
@@ -131,14 +144,31 @@ func (t *Transport) disableCompression() bool { | |||||
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) | return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) | ||||
} | } | ||||
func (t *Transport) pingTimeout() time.Duration { | |||||
if t.PingTimeout == 0 { | |||||
return 15 * time.Second | |||||
} | |||||
return t.PingTimeout | |||||
} | |||||
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. | // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. | ||||
// It returns an error if t1 has already been HTTP/2-enabled. | // It returns an error if t1 has already been HTTP/2-enabled. | ||||
// | |||||
// Use ConfigureTransports instead to configure the HTTP/2 Transport. | |||||
func ConfigureTransport(t1 *http.Transport) error { | func ConfigureTransport(t1 *http.Transport) error { | ||||
_, err := configureTransport(t1) | |||||
_, err := ConfigureTransports(t1) | |||||
return err | return err | ||||
} | } | ||||
func configureTransport(t1 *http.Transport) (*Transport, error) { | |||||
// ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2. | |||||
// It returns a new HTTP/2 Transport for further configuration. | |||||
// It returns an error if t1 has already been HTTP/2-enabled. | |||||
func ConfigureTransports(t1 *http.Transport) (*Transport, error) { | |||||
return configureTransports(t1) | |||||
} | |||||
func configureTransports(t1 *http.Transport) (*Transport, error) { | |||||
connPool := new(clientConnPool) | connPool := new(clientConnPool) | ||||
t2 := &Transport{ | t2 := &Transport{ | ||||
ConnPool: noDialClientConnPool{connPool}, | ConnPool: noDialClientConnPool{connPool}, | ||||
@@ -668,6 +698,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro | |||||
cc.inflow.add(transportDefaultConnFlow + initialWindowSize) | cc.inflow.add(transportDefaultConnFlow + initialWindowSize) | ||||
cc.bw.Flush() | cc.bw.Flush() | ||||
if cc.werr != nil { | if cc.werr != nil { | ||||
cc.Close() | |||||
return nil, cc.werr | return nil, cc.werr | ||||
} | } | ||||
@@ -675,6 +706,20 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro | |||||
return cc, nil | return cc, nil | ||||
} | } | ||||
func (cc *ClientConn) healthCheck() { | |||||
pingTimeout := cc.t.pingTimeout() | |||||
// We don't need to periodically ping in the health check, because the readLoop of ClientConn will | |||||
// trigger the healthCheck again if there is no frame received. | |||||
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) | |||||
defer cancel() | |||||
err := cc.Ping(ctx) | |||||
if err != nil { | |||||
cc.closeForLostPing() | |||||
cc.t.connPool().MarkDead(cc) | |||||
return | |||||
} | |||||
} | |||||
func (cc *ClientConn) setGoAway(f *GoAwayFrame) { | func (cc *ClientConn) setGoAway(f *GoAwayFrame) { | ||||
cc.mu.Lock() | cc.mu.Lock() | ||||
defer cc.mu.Unlock() | defer cc.mu.Unlock() | ||||
@@ -846,14 +891,12 @@ func (cc *ClientConn) sendGoAway() error { | |||||
return nil | return nil | ||||
} | } | ||||
// Close closes the client connection immediately. | |||||
// | |||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. | |||||
func (cc *ClientConn) Close() error { | |||||
// closes the client connection immediately. In-flight requests are interrupted. | |||||
// err is sent to streams. | |||||
func (cc *ClientConn) closeForError(err error) error { | |||||
cc.mu.Lock() | cc.mu.Lock() | ||||
defer cc.cond.Broadcast() | defer cc.cond.Broadcast() | ||||
defer cc.mu.Unlock() | defer cc.mu.Unlock() | ||||
err := errors.New("http2: client connection force closed via ClientConn.Close") | |||||
for id, cs := range cc.streams { | for id, cs := range cc.streams { | ||||
select { | select { | ||||
case cs.resc <- resAndError{err: err}: | case cs.resc <- resAndError{err: err}: | ||||
@@ -866,6 +909,20 @@ func (cc *ClientConn) Close() error { | |||||
return cc.tconn.Close() | return cc.tconn.Close() | ||||
} | } | ||||
// Close closes the client connection immediately. | |||||
// | |||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. | |||||
func (cc *ClientConn) Close() error { | |||||
err := errors.New("http2: client connection force closed via ClientConn.Close") | |||||
return cc.closeForError(err) | |||||
} | |||||
// closes the client connection immediately. In-flight requests are interrupted. | |||||
func (cc *ClientConn) closeForLostPing() error { | |||||
err := errors.New("http2: client connection lost") | |||||
return cc.closeForError(err) | |||||
} | |||||
const maxAllocFrameSize = 512 << 10 | const maxAllocFrameSize = 512 << 10 | ||||
// frameBuffer returns a scratch buffer suitable for writing DATA frames. | // frameBuffer returns a scratch buffer suitable for writing DATA frames. | ||||
@@ -1033,6 +1090,15 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
bodyWriter := cc.t.getBodyWriterState(cs, body) | bodyWriter := cc.t.getBodyWriterState(cs, body) | ||||
cs.on100 = bodyWriter.on100 | cs.on100 = bodyWriter.on100 | ||||
defer func() { | |||||
cc.wmu.Lock() | |||||
werr := cc.werr | |||||
cc.wmu.Unlock() | |||||
if werr != nil { | |||||
cc.Close() | |||||
} | |||||
}() | |||||
cc.wmu.Lock() | cc.wmu.Lock() | ||||
endStream := !hasBody && !hasTrailers | endStream := !hasBody && !hasTrailers | ||||
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) | werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) | ||||
@@ -1082,6 +1148,9 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
// we can keep it. | // we can keep it. | ||||
bodyWriter.cancel() | bodyWriter.cancel() | ||||
cs.abortRequestBodyWrite(errStopReqBodyWrite) | cs.abortRequestBodyWrite(errStopReqBodyWrite) | ||||
if hasBody && !bodyWritten { | |||||
<-bodyWriter.resc | |||||
} | |||||
} | } | ||||
if re.err != nil { | if re.err != nil { | ||||
cc.forgetStreamID(cs.ID) | cc.forgetStreamID(cs.ID) | ||||
@@ -1102,6 +1171,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
} else { | } else { | ||||
bodyWriter.cancel() | bodyWriter.cancel() | ||||
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) | cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) | ||||
<-bodyWriter.resc | |||||
} | } | ||||
cc.forgetStreamID(cs.ID) | cc.forgetStreamID(cs.ID) | ||||
return nil, cs.getStartedWrite(), errTimeout | return nil, cs.getStartedWrite(), errTimeout | ||||
@@ -1111,6 +1181,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
} else { | } else { | ||||
bodyWriter.cancel() | bodyWriter.cancel() | ||||
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) | cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) | ||||
<-bodyWriter.resc | |||||
} | } | ||||
cc.forgetStreamID(cs.ID) | cc.forgetStreamID(cs.ID) | ||||
return nil, cs.getStartedWrite(), ctx.Err() | return nil, cs.getStartedWrite(), ctx.Err() | ||||
@@ -1120,6 +1191,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
} else { | } else { | ||||
bodyWriter.cancel() | bodyWriter.cancel() | ||||
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) | cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) | ||||
<-bodyWriter.resc | |||||
} | } | ||||
cc.forgetStreamID(cs.ID) | cc.forgetStreamID(cs.ID) | ||||
return nil, cs.getStartedWrite(), errRequestCanceled | return nil, cs.getStartedWrite(), errRequestCanceled | ||||
@@ -1129,6 +1201,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
// forgetStreamID. | // forgetStreamID. | ||||
return nil, cs.getStartedWrite(), cs.resetErr | return nil, cs.getStartedWrite(), cs.resetErr | ||||
case err := <-bodyWriter.resc: | case err := <-bodyWriter.resc: | ||||
bodyWritten = true | |||||
// Prefer the read loop's response, if available. Issue 16102. | // Prefer the read loop's response, if available. Issue 16102. | ||||
select { | select { | ||||
case re := <-readLoopResCh: | case re := <-readLoopResCh: | ||||
@@ -1139,7 +1212,6 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf | |||||
cc.forgetStreamID(cs.ID) | cc.forgetStreamID(cs.ID) | ||||
return nil, cs.getStartedWrite(), err | return nil, cs.getStartedWrite(), err | ||||
} | } | ||||
bodyWritten = true | |||||
if d := cc.responseHeaderTimeout(); d != 0 { | if d := cc.responseHeaderTimeout(); d != 0 { | ||||
timer := time.NewTimer(d) | timer := time.NewTimer(d) | ||||
defer timer.Stop() | defer timer.Stop() | ||||
@@ -1737,8 +1809,17 @@ func (rl *clientConnReadLoop) run() error { | |||||
rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse | rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse | ||||
gotReply := false // ever saw a HEADERS reply | gotReply := false // ever saw a HEADERS reply | ||||
gotSettings := false | gotSettings := false | ||||
readIdleTimeout := cc.t.ReadIdleTimeout | |||||
var t *time.Timer | |||||
if readIdleTimeout != 0 { | |||||
t = time.AfterFunc(readIdleTimeout, cc.healthCheck) | |||||
defer t.Stop() | |||||
} | |||||
for { | for { | ||||
f, err := cc.fr.ReadFrame() | f, err := cc.fr.ReadFrame() | ||||
if t != nil { | |||||
t.Reset(readIdleTimeout) | |||||
} | |||||
if err != nil { | if err != nil { | ||||
cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) | cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) | ||||
} | } | ||||
@@ -1950,8 +2031,8 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra | |||||
if !streamEnded || isHead { | if !streamEnded || isHead { | ||||
res.ContentLength = -1 | res.ContentLength = -1 | ||||
if clens := res.Header["Content-Length"]; len(clens) == 1 { | if clens := res.Header["Content-Length"]; len(clens) == 1 { | ||||
if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil { | |||||
res.ContentLength = clen64 | |||||
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil { | |||||
res.ContentLength = int64(cl) | |||||
} else { | } else { | ||||
// TODO: care? unlike http/1, it won't mess up our framing, so it's | // TODO: care? unlike http/1, it won't mess up our framing, so it's | ||||
// more safe smuggling-wise to ignore. | // more safe smuggling-wise to ignore. | ||||
@@ -2469,6 +2550,7 @@ func strSliceContains(ss []string, s string) bool { | |||||
type erringRoundTripper struct{ err error } | type erringRoundTripper struct{ err error } | ||||
func (rt erringRoundTripper) RoundTripErr() error { return rt.err } | |||||
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err } | func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err } | ||||
// gzipReader wraps a response body so it can lazily | // gzipReader wraps a response body so it can lazily | ||||
@@ -2550,7 +2632,9 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body | |||||
func (s bodyWriterState) cancel() { | func (s bodyWriterState) cancel() { | ||||
if s.timer != nil { | if s.timer != nil { | ||||
s.timer.Stop() | |||||
if s.timer.Stop() { | |||||
s.resc <- nil | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -4,6 +4,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build go1.10 | |||||
// +build go1.10 | // +build go1.10 | ||||
// Package idna implements IDNA2008 using the compatibility processing | // Package idna implements IDNA2008 using the compatibility processing | ||||
@@ -4,6 +4,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build !go1.10 | |||||
// +build !go1.10 | // +build !go1.10 | ||||
// Package idna implements IDNA2008 using the compatibility processing | // Package idna implements IDNA2008 using the compatibility processing | ||||
@@ -1,5 +1,6 @@ | |||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | ||||
//go:build go1.10 && !go1.13 | |||||
// +build go1.10,!go1.13 | // +build go1.10,!go1.13 | ||||
package idna | package idna | ||||
@@ -1,5 +1,6 @@ | |||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | ||||
//go:build go1.13 && !go1.14 | |||||
// +build go1.13,!go1.14 | // +build go1.13,!go1.14 | ||||
package idna | package idna | ||||
@@ -1,6 +1,7 @@ | |||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | ||||
// +build go1.14 | |||||
//go:build go1.14 && !go1.16 | |||||
// +build go1.14,!go1.16 | |||||
package idna | package idna | ||||
@@ -1,5 +1,6 @@ | |||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | ||||
//go:build !go1.10 | |||||
// +build !go1.10 | // +build !go1.10 | ||||
package idna | package idna | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -39,20 +39,25 @@ func (bigEndian) Uint64(b []byte) uint64 { | |||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 | ||||
} | } | ||||
// hostByteOrder returns binary.LittleEndian on little-endian machines and | |||||
// binary.BigEndian on big-endian machines. | |||||
// hostByteOrder returns littleEndian on little-endian machines and | |||||
// bigEndian on big-endian machines. | |||||
func hostByteOrder() byteOrder { | func hostByteOrder() byteOrder { | ||||
switch runtime.GOARCH { | switch runtime.GOARCH { | ||||
case "386", "amd64", "amd64p32", | case "386", "amd64", "amd64p32", | ||||
"alpha", | |||||
"arm", "arm64", | "arm", "arm64", | ||||
"mipsle", "mips64le", "mips64p32le", | "mipsle", "mips64le", "mips64p32le", | ||||
"nios2", | |||||
"ppc64le", | "ppc64le", | ||||
"riscv", "riscv64": | |||||
"riscv", "riscv64", | |||||
"sh": | |||||
return littleEndian{} | return littleEndian{} | ||||
case "armbe", "arm64be", | case "armbe", "arm64be", | ||||
"m68k", | |||||
"mips", "mips64", "mips64p32", | "mips", "mips64", "mips64p32", | ||||
"ppc", "ppc64", | "ppc", "ppc64", | ||||
"s390", "s390x", | "s390", "s390x", | ||||
"shbe", | |||||
"sparc", "sparc64": | "sparc", "sparc64": | ||||
return bigEndian{} | return bigEndian{} | ||||
} | } | ||||
@@ -6,6 +6,11 @@ | |||||
// various CPU architectures. | // various CPU architectures. | ||||
package cpu | package cpu | ||||
import ( | |||||
"os" | |||||
"strings" | |||||
) | |||||
// Initialized reports whether the CPU features were initialized. | // Initialized reports whether the CPU features were initialized. | ||||
// | // | ||||
// For some GOOS/GOARCH combinations initialization of the CPU features depends | // For some GOOS/GOARCH combinations initialization of the CPU features depends | ||||
@@ -24,26 +29,46 @@ type CacheLinePad struct{ _ [cacheLineSize]byte } | |||||
// and HasAVX2 are only set if the OS supports XMM and YMM | // and HasAVX2 are only set if the OS supports XMM and YMM | ||||
// registers in addition to the CPUID feature bit being set. | // registers in addition to the CPUID feature bit being set. | ||||
var X86 struct { | var X86 struct { | ||||
_ CacheLinePad | |||||
HasAES bool // AES hardware implementation (AES NI) | |||||
HasADX bool // Multi-precision add-carry instruction extensions | |||||
HasAVX bool // Advanced vector extension | |||||
HasAVX2 bool // Advanced vector extension 2 | |||||
HasBMI1 bool // Bit manipulation instruction set 1 | |||||
HasBMI2 bool // Bit manipulation instruction set 2 | |||||
HasERMS bool // Enhanced REP for MOVSB and STOSB | |||||
HasFMA bool // Fused-multiply-add instructions | |||||
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. | |||||
HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM | |||||
HasPOPCNT bool // Hamming weight instruction POPCNT. | |||||
HasRDRAND bool // RDRAND instruction (on-chip random number generator) | |||||
HasRDSEED bool // RDSEED instruction (on-chip random number generator) | |||||
HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64) | |||||
HasSSE3 bool // Streaming SIMD extension 3 | |||||
HasSSSE3 bool // Supplemental streaming SIMD extension 3 | |||||
HasSSE41 bool // Streaming SIMD extension 4 and 4.1 | |||||
HasSSE42 bool // Streaming SIMD extension 4 and 4.2 | |||||
_ CacheLinePad | |||||
_ CacheLinePad | |||||
HasAES bool // AES hardware implementation (AES NI) | |||||
HasADX bool // Multi-precision add-carry instruction extensions | |||||
HasAVX bool // Advanced vector extension | |||||
HasAVX2 bool // Advanced vector extension 2 | |||||
HasAVX512 bool // Advanced vector extension 512 | |||||
HasAVX512F bool // Advanced vector extension 512 Foundation Instructions | |||||
HasAVX512CD bool // Advanced vector extension 512 Conflict Detection Instructions | |||||
HasAVX512ER bool // Advanced vector extension 512 Exponential and Reciprocal Instructions | |||||
HasAVX512PF bool // Advanced vector extension 512 Prefetch Instructions Instructions | |||||
HasAVX512VL bool // Advanced vector extension 512 Vector Length Extensions | |||||
HasAVX512BW bool // Advanced vector extension 512 Byte and Word Instructions | |||||
HasAVX512DQ bool // Advanced vector extension 512 Doubleword and Quadword Instructions | |||||
HasAVX512IFMA bool // Advanced vector extension 512 Integer Fused Multiply Add | |||||
HasAVX512VBMI bool // Advanced vector extension 512 Vector Byte Manipulation Instructions | |||||
HasAVX5124VNNIW bool // Advanced vector extension 512 Vector Neural Network Instructions Word variable precision | |||||
HasAVX5124FMAPS bool // Advanced vector extension 512 Fused Multiply Accumulation Packed Single precision | |||||
HasAVX512VPOPCNTDQ bool // Advanced vector extension 512 Double and quad word population count instructions | |||||
HasAVX512VPCLMULQDQ bool // Advanced vector extension 512 Vector carry-less multiply operations | |||||
HasAVX512VNNI bool // Advanced vector extension 512 Vector Neural Network Instructions | |||||
HasAVX512GFNI bool // Advanced vector extension 512 Galois field New Instructions | |||||
HasAVX512VAES bool // Advanced vector extension 512 Vector AES instructions | |||||
HasAVX512VBMI2 bool // Advanced vector extension 512 Vector Byte Manipulation Instructions 2 | |||||
HasAVX512BITALG bool // Advanced vector extension 512 Bit Algorithms | |||||
HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions | |||||
HasBMI1 bool // Bit manipulation instruction set 1 | |||||
HasBMI2 bool // Bit manipulation instruction set 2 | |||||
HasERMS bool // Enhanced REP for MOVSB and STOSB | |||||
HasFMA bool // Fused-multiply-add instructions | |||||
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. | |||||
HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM | |||||
HasPOPCNT bool // Hamming weight instruction POPCNT. | |||||
HasRDRAND bool // RDRAND instruction (on-chip random number generator) | |||||
HasRDSEED bool // RDSEED instruction (on-chip random number generator) | |||||
HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64) | |||||
HasSSE3 bool // Streaming SIMD extension 3 | |||||
HasSSSE3 bool // Supplemental streaming SIMD extension 3 | |||||
HasSSE41 bool // Streaming SIMD extension 4 and 4.1 | |||||
HasSSE42 bool // Streaming SIMD extension 4 and 4.2 | |||||
_ CacheLinePad | |||||
} | } | ||||
// ARM64 contains the supported CPU features of the | // ARM64 contains the supported CPU features of the | ||||
@@ -169,3 +194,94 @@ var S390X struct { | |||||
HasVXE bool // vector-enhancements facility 1 | HasVXE bool // vector-enhancements facility 1 | ||||
_ CacheLinePad | _ CacheLinePad | ||||
} | } | ||||
func init() { | |||||
archInit() | |||||
initOptions() | |||||
processOptions() | |||||
} | |||||
// options contains the cpu debug options that can be used in GODEBUG. | |||||
// Options are arch dependent and are added by the arch specific initOptions functions. | |||||
// Features that are mandatory for the specific GOARCH should have the Required field set | |||||
// (e.g. SSE2 on amd64). | |||||
var options []option | |||||
// Option names should be lower case. e.g. avx instead of AVX. | |||||
type option struct { | |||||
Name string | |||||
Feature *bool | |||||
Specified bool // whether feature value was specified in GODEBUG | |||||
Enable bool // whether feature should be enabled | |||||
Required bool // whether feature is mandatory and can not be disabled | |||||
} | |||||
func processOptions() { | |||||
env := os.Getenv("GODEBUG") | |||||
field: | |||||
for env != "" { | |||||
field := "" | |||||
i := strings.IndexByte(env, ',') | |||||
if i < 0 { | |||||
field, env = env, "" | |||||
} else { | |||||
field, env = env[:i], env[i+1:] | |||||
} | |||||
if len(field) < 4 || field[:4] != "cpu." { | |||||
continue | |||||
} | |||||
i = strings.IndexByte(field, '=') | |||||
if i < 0 { | |||||
print("GODEBUG sys/cpu: no value specified for \"", field, "\"\n") | |||||
continue | |||||
} | |||||
key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on" | |||||
var enable bool | |||||
switch value { | |||||
case "on": | |||||
enable = true | |||||
case "off": | |||||
enable = false | |||||
default: | |||||
print("GODEBUG sys/cpu: value \"", value, "\" not supported for cpu option \"", key, "\"\n") | |||||
continue field | |||||
} | |||||
if key == "all" { | |||||
for i := range options { | |||||
options[i].Specified = true | |||||
options[i].Enable = enable || options[i].Required | |||||
} | |||||
continue field | |||||
} | |||||
for i := range options { | |||||
if options[i].Name == key { | |||||
options[i].Specified = true | |||||
options[i].Enable = enable | |||||
continue field | |||||
} | |||||
} | |||||
print("GODEBUG sys/cpu: unknown cpu feature \"", key, "\"\n") | |||||
} | |||||
for _, o := range options { | |||||
if !o.Specified { | |||||
continue | |||||
} | |||||
if o.Enable && !*o.Feature { | |||||
print("GODEBUG sys/cpu: can not enable \"", o.Name, "\", missing CPU support\n") | |||||
continue | |||||
} | |||||
if !o.Enable && o.Required { | |||||
print("GODEBUG sys/cpu: can not disable \"", o.Name, "\", required CPU feature\n") | |||||
continue | |||||
} | |||||
*o.Feature = o.Enable | |||||
} | |||||
} |
@@ -2,12 +2,11 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build aix,ppc64 | |||||
//go:build aix | |||||
// +build aix | |||||
package cpu | package cpu | ||||
const cacheLineSize = 128 | |||||
const ( | const ( | ||||
// getsystemcfg constants | // getsystemcfg constants | ||||
_SC_IMPL = 2 | _SC_IMPL = 2 | ||||
@@ -15,7 +14,7 @@ const ( | |||||
_IMPL_POWER9 = 0x20000 | _IMPL_POWER9 = 0x20000 | ||||
) | ) | ||||
func init() { | |||||
func archInit() { | |||||
impl := getsystemcfg(_SC_IMPL) | impl := getsystemcfg(_SC_IMPL) | ||||
if impl&_IMPL_POWER8 != 0 { | if impl&_IMPL_POWER8 != 0 { | ||||
PPC64.IsPOWER8 = true | PPC64.IsPOWER8 = true |
@@ -38,3 +38,36 @@ const ( | |||||
hwcap2_SHA2 = 1 << 3 | hwcap2_SHA2 = 1 << 3 | ||||
hwcap2_CRC32 = 1 << 4 | hwcap2_CRC32 = 1 << 4 | ||||
) | ) | ||||
func initOptions() { | |||||
options = []option{ | |||||
{Name: "pmull", Feature: &ARM.HasPMULL}, | |||||
{Name: "sha1", Feature: &ARM.HasSHA1}, | |||||
{Name: "sha2", Feature: &ARM.HasSHA2}, | |||||
{Name: "swp", Feature: &ARM.HasSWP}, | |||||
{Name: "thumb", Feature: &ARM.HasTHUMB}, | |||||
{Name: "thumbee", Feature: &ARM.HasTHUMBEE}, | |||||
{Name: "tls", Feature: &ARM.HasTLS}, | |||||
{Name: "vfp", Feature: &ARM.HasVFP}, | |||||
{Name: "vfpd32", Feature: &ARM.HasVFPD32}, | |||||
{Name: "vfpv3", Feature: &ARM.HasVFPv3}, | |||||
{Name: "vfpv3d16", Feature: &ARM.HasVFPv3D16}, | |||||
{Name: "vfpv4", Feature: &ARM.HasVFPv4}, | |||||
{Name: "half", Feature: &ARM.HasHALF}, | |||||
{Name: "26bit", Feature: &ARM.Has26BIT}, | |||||
{Name: "fastmul", Feature: &ARM.HasFASTMUL}, | |||||
{Name: "fpa", Feature: &ARM.HasFPA}, | |||||
{Name: "edsp", Feature: &ARM.HasEDSP}, | |||||
{Name: "java", Feature: &ARM.HasJAVA}, | |||||
{Name: "iwmmxt", Feature: &ARM.HasIWMMXT}, | |||||
{Name: "crunch", Feature: &ARM.HasCRUNCH}, | |||||
{Name: "neon", Feature: &ARM.HasNEON}, | |||||
{Name: "idivt", Feature: &ARM.HasIDIVT}, | |||||
{Name: "idiva", Feature: &ARM.HasIDIVA}, | |||||
{Name: "lpae", Feature: &ARM.HasLPAE}, | |||||
{Name: "evtstrm", Feature: &ARM.HasEVTSTRM}, | |||||
{Name: "aes", Feature: &ARM.HasAES}, | |||||
{Name: "crc32", Feature: &ARM.HasCRC32}, | |||||
} | |||||
} |
@@ -8,27 +8,65 @@ import "runtime" | |||||
const cacheLineSize = 64 | const cacheLineSize = 64 | ||||
func init() { | |||||
func initOptions() { | |||||
options = []option{ | |||||
{Name: "fp", Feature: &ARM64.HasFP}, | |||||
{Name: "asimd", Feature: &ARM64.HasASIMD}, | |||||
{Name: "evstrm", Feature: &ARM64.HasEVTSTRM}, | |||||
{Name: "aes", Feature: &ARM64.HasAES}, | |||||
{Name: "fphp", Feature: &ARM64.HasFPHP}, | |||||
{Name: "jscvt", Feature: &ARM64.HasJSCVT}, | |||||
{Name: "lrcpc", Feature: &ARM64.HasLRCPC}, | |||||
{Name: "pmull", Feature: &ARM64.HasPMULL}, | |||||
{Name: "sha1", Feature: &ARM64.HasSHA1}, | |||||
{Name: "sha2", Feature: &ARM64.HasSHA2}, | |||||
{Name: "sha3", Feature: &ARM64.HasSHA3}, | |||||
{Name: "sha512", Feature: &ARM64.HasSHA512}, | |||||
{Name: "sm3", Feature: &ARM64.HasSM3}, | |||||
{Name: "sm4", Feature: &ARM64.HasSM4}, | |||||
{Name: "sve", Feature: &ARM64.HasSVE}, | |||||
{Name: "crc32", Feature: &ARM64.HasCRC32}, | |||||
{Name: "atomics", Feature: &ARM64.HasATOMICS}, | |||||
{Name: "asimdhp", Feature: &ARM64.HasASIMDHP}, | |||||
{Name: "cpuid", Feature: &ARM64.HasCPUID}, | |||||
{Name: "asimrdm", Feature: &ARM64.HasASIMDRDM}, | |||||
{Name: "fcma", Feature: &ARM64.HasFCMA}, | |||||
{Name: "dcpop", Feature: &ARM64.HasDCPOP}, | |||||
{Name: "asimddp", Feature: &ARM64.HasASIMDDP}, | |||||
{Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM}, | |||||
} | |||||
} | |||||
func archInit() { | |||||
switch runtime.GOOS { | switch runtime.GOOS { | ||||
case "android", "darwin": | |||||
// Android and iOS don't seem to allow reading these registers. | |||||
// Fake the minimal features expected by | |||||
// TestARM64minimalFeatures. | |||||
ARM64.HasASIMD = true | |||||
ARM64.HasFP = true | |||||
case "linux": | |||||
case "freebsd": | |||||
readARM64Registers() | |||||
case "linux", "netbsd": | |||||
doinit() | doinit() | ||||
default: | default: | ||||
readARM64Registers() | |||||
// Most platforms don't seem to allow reading these registers. | |||||
// | |||||
// OpenBSD: | |||||
// See https://golang.org/issue/31746 | |||||
setMinimalFeatures() | |||||
} | } | ||||
} | } | ||||
// setMinimalFeatures fakes the minimal ARM64 features expected by | |||||
// TestARM64minimalFeatures. | |||||
func setMinimalFeatures() { | |||||
ARM64.HasASIMD = true | |||||
ARM64.HasFP = true | |||||
} | |||||
func readARM64Registers() { | func readARM64Registers() { | ||||
Initialized = true | Initialized = true | ||||
// ID_AA64ISAR0_EL1 | |||||
isar0 := getisar0() | |||||
parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0()) | |||||
} | |||||
func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { | |||||
// ID_AA64ISAR0_EL1 | |||||
switch extractBits(isar0, 4, 7) { | switch extractBits(isar0, 4, 7) { | ||||
case 1: | case 1: | ||||
ARM64.HasAES = true | ARM64.HasAES = true | ||||
@@ -86,8 +124,6 @@ func readARM64Registers() { | |||||
} | } | ||||
// ID_AA64ISAR1_EL1 | // ID_AA64ISAR1_EL1 | ||||
isar1 := getisar1() | |||||
switch extractBits(isar1, 0, 3) { | switch extractBits(isar1, 0, 3) { | ||||
case 1: | case 1: | ||||
ARM64.HasDCPOP = true | ARM64.HasDCPOP = true | ||||
@@ -109,8 +145,6 @@ func readARM64Registers() { | |||||
} | } | ||||
// ID_AA64PFR0_EL1 | // ID_AA64PFR0_EL1 | ||||
pfr0 := getpfr0() | |||||
switch extractBits(pfr0, 16, 19) { | switch extractBits(pfr0, 16, 19) { | ||||
case 0: | case 0: | ||||
ARM64.HasFP = true | ARM64.HasFP = true | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
package cpu | package cpu | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
package cpu | package cpu | ||||
@@ -2,8 +2,9 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build (386 || amd64 || amd64p32) && gc | |||||
// +build 386 amd64 amd64p32 | // +build 386 amd64 amd64p32 | ||||
// +build !gccgo | |||||
// +build gc | |||||
package cpu | package cpu | ||||
@@ -14,3 +15,7 @@ func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) | |||||
// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler | // xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler | ||||
// and in cpu_gccgo.c for gccgo. | // and in cpu_gccgo.c for gccgo. | ||||
func xgetbv() (eax, edx uint32) | func xgetbv() (eax, edx uint32) | ||||
// darwinSupportsAVX512 is implemented in cpu_x86.s for gc compiler | |||||
// and in cpu_gccgo_x86.go for gccgo. | |||||
func darwinSupportsAVX512() bool |
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build gccgo | |||||
// +build gccgo | // +build gccgo | ||||
package cpu | package cpu | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build gccgo | |||||
// +build gccgo | // +build gccgo | ||||
package cpu | package cpu | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build (386 || amd64 || amd64p32) && gccgo | |||||
// +build 386 amd64 amd64p32 | // +build 386 amd64 amd64p32 | ||||
// +build gccgo | // +build gccgo | ||||
@@ -24,3 +25,9 @@ func xgetbv() (eax, edx uint32) { | |||||
gccgoXgetbv(&a, &d) | gccgoXgetbv(&a, &d) | ||||
return a, d | return a, d | ||||
} | } | ||||
// gccgo doesn't build on Darwin, per: | |||||
// https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gcc.rb#L76 | |||||
func darwinSupportsAVX512() bool { | |||||
return false | |||||
} |
@@ -2,11 +2,12 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build !386 && !amd64 && !amd64p32 && !arm64 | |||||
// +build !386,!amd64,!amd64p32,!arm64 | // +build !386,!amd64,!amd64p32,!arm64 | ||||
package cpu | package cpu | ||||
func init() { | |||||
func archInit() { | |||||
if err := readHWCAP(); err != nil { | if err := readHWCAP(); err != nil { | ||||
return | return | ||||
} | } | ||||
@@ -2,6 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && (mips64 || mips64le) | |||||
// +build linux | |||||
// +build mips64 mips64le | // +build mips64 mips64le | ||||
package cpu | package cpu | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x | |||||
// +build linux,!arm,!arm64,!mips64,!mips64le,!ppc64,!ppc64le,!s390x | // +build linux,!arm,!arm64,!mips64,!mips64le,!ppc64,!ppc64le,!s390x | ||||
package cpu | package cpu | ||||
@@ -2,13 +2,12 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && (ppc64 || ppc64le) | |||||
// +build linux | // +build linux | ||||
// +build ppc64 ppc64le | // +build ppc64 ppc64le | ||||
package cpu | package cpu | ||||
const cacheLineSize = 128 | |||||
// HWCAP/HWCAP2 bits. These are exposed by the kernel. | // HWCAP/HWCAP2 bits. These are exposed by the kernel. | ||||
const ( | const ( | ||||
// ISA Level | // ISA Level | ||||
@@ -4,8 +4,6 @@ | |||||
package cpu | package cpu | ||||
const cacheLineSize = 256 | |||||
const ( | const ( | ||||
// bit mask values from /usr/include/bits/hwcap.h | // bit mask values from /usr/include/bits/hwcap.h | ||||
hwcap_ZARCH = 2 | hwcap_ZARCH = 2 | ||||
@@ -19,86 +17,7 @@ const ( | |||||
hwcap_VXE = 8192 | hwcap_VXE = 8192 | ||||
) | ) | ||||
// bitIsSet reports whether the bit at index is set. The bit index | |||||
// is in big endian order, so bit index 0 is the leftmost bit. | |||||
func bitIsSet(bits []uint64, index uint) bool { | |||||
return bits[index/64]&((1<<63)>>(index%64)) != 0 | |||||
} | |||||
// function is the code for the named cryptographic function. | |||||
type function uint8 | |||||
const ( | |||||
// KM{,A,C,CTR} function codes | |||||
aes128 function = 18 // AES-128 | |||||
aes192 function = 19 // AES-192 | |||||
aes256 function = 20 // AES-256 | |||||
// K{I,L}MD function codes | |||||
sha1 function = 1 // SHA-1 | |||||
sha256 function = 2 // SHA-256 | |||||
sha512 function = 3 // SHA-512 | |||||
sha3_224 function = 32 // SHA3-224 | |||||
sha3_256 function = 33 // SHA3-256 | |||||
sha3_384 function = 34 // SHA3-384 | |||||
sha3_512 function = 35 // SHA3-512 | |||||
shake128 function = 36 // SHAKE-128 | |||||
shake256 function = 37 // SHAKE-256 | |||||
// KLMD function codes | |||||
ghash function = 65 // GHASH | |||||
) | |||||
// queryResult contains the result of a Query function | |||||
// call. Bits are numbered in big endian order so the | |||||
// leftmost bit (the MSB) is at index 0. | |||||
type queryResult struct { | |||||
bits [2]uint64 | |||||
} | |||||
// Has reports whether the given functions are present. | |||||
func (q *queryResult) Has(fns ...function) bool { | |||||
if len(fns) == 0 { | |||||
panic("no function codes provided") | |||||
} | |||||
for _, f := range fns { | |||||
if !bitIsSet(q.bits[:], uint(f)) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
// facility is a bit index for the named facility. | |||||
type facility uint8 | |||||
const ( | |||||
// cryptography facilities | |||||
msa4 facility = 77 // message-security-assist extension 4 | |||||
msa8 facility = 146 // message-security-assist extension 8 | |||||
) | |||||
// facilityList contains the result of an STFLE call. | |||||
// Bits are numbered in big endian order so the | |||||
// leftmost bit (the MSB) is at index 0. | |||||
type facilityList struct { | |||||
bits [4]uint64 | |||||
} | |||||
// Has reports whether the given facilities are present. | |||||
func (s *facilityList) Has(fs ...facility) bool { | |||||
if len(fs) == 0 { | |||||
panic("no facility bits provided") | |||||
} | |||||
for _, f := range fs { | |||||
if !bitIsSet(s.bits[:], uint(f)) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
func doinit() { | |||||
func initS390Xbase() { | |||||
// test HWCAP bit vector | // test HWCAP bit vector | ||||
has := func(featureMask uint) bool { | has := func(featureMask uint) bool { | ||||
return hwCap&featureMask == featureMask | return hwCap&featureMask == featureMask | ||||
@@ -118,44 +37,4 @@ func doinit() { | |||||
if S390X.HasVX { | if S390X.HasVX { | ||||
S390X.HasVXE = has(hwcap_VXE) | S390X.HasVXE = has(hwcap_VXE) | ||||
} | } | ||||
// We need implementations of stfle, km and so on | |||||
// to detect cryptographic features. | |||||
if !haveAsmFunctions() { | |||||
return | |||||
} | |||||
// optional cryptographic functions | |||||
if S390X.HasMSA { | |||||
aes := []function{aes128, aes192, aes256} | |||||
// cipher message | |||||
km, kmc := kmQuery(), kmcQuery() | |||||
S390X.HasAES = km.Has(aes...) | |||||
S390X.HasAESCBC = kmc.Has(aes...) | |||||
if S390X.HasSTFLE { | |||||
facilities := stfle() | |||||
if facilities.Has(msa4) { | |||||
kmctr := kmctrQuery() | |||||
S390X.HasAESCTR = kmctr.Has(aes...) | |||||
} | |||||
if facilities.Has(msa8) { | |||||
kma := kmaQuery() | |||||
S390X.HasAESGCM = kma.Has(aes...) | |||||
} | |||||
} | |||||
// compute message digest | |||||
kimd := kimdQuery() // intermediate (no padding) | |||||
klmd := klmdQuery() // last (padding) | |||||
S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) | |||||
S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) | |||||
S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) | |||||
S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist | |||||
sha3 := []function{ | |||||
sha3_224, sha3_256, sha3_384, sha3_512, | |||||
shake128, shake256, | |||||
} | |||||
S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...) | |||||
} | |||||
} | } |
@@ -2,8 +2,15 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build mips64 || mips64le | |||||
// +build mips64 mips64le | // +build mips64 mips64le | ||||
package cpu | package cpu | ||||
const cacheLineSize = 32 | const cacheLineSize = 32 | ||||
func initOptions() { | |||||
options = []option{ | |||||
{Name: "msa", Feature: &MIPS64X.HasMSA}, | |||||
} | |||||
} |
@@ -2,8 +2,11 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build mips || mipsle | |||||
// +build mips mipsle | // +build mips mipsle | ||||
package cpu | package cpu | ||||
const cacheLineSize = 32 | const cacheLineSize = 32 | ||||
func initOptions() {} |
@@ -0,0 +1,173 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
package cpu | |||||
import ( | |||||
"syscall" | |||||
"unsafe" | |||||
) | |||||
// Minimal copy of functionality from x/sys/unix so the cpu package can call | |||||
// sysctl without depending on x/sys/unix. | |||||
const ( | |||||
_CTL_QUERY = -2 | |||||
_SYSCTL_VERS_1 = 0x1000000 | |||||
) | |||||
var _zero uintptr | |||||
func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { | |||||
var _p0 unsafe.Pointer | |||||
if len(mib) > 0 { | |||||
_p0 = unsafe.Pointer(&mib[0]) | |||||
} else { | |||||
_p0 = unsafe.Pointer(&_zero) | |||||
} | |||||
_, _, errno := syscall.Syscall6( | |||||
syscall.SYS___SYSCTL, | |||||
uintptr(_p0), | |||||
uintptr(len(mib)), | |||||
uintptr(unsafe.Pointer(old)), | |||||
uintptr(unsafe.Pointer(oldlen)), | |||||
uintptr(unsafe.Pointer(new)), | |||||
uintptr(newlen)) | |||||
if errno != 0 { | |||||
return errno | |||||
} | |||||
return nil | |||||
} | |||||
type sysctlNode struct { | |||||
Flags uint32 | |||||
Num int32 | |||||
Name [32]int8 | |||||
Ver uint32 | |||||
__rsvd uint32 | |||||
Un [16]byte | |||||
_sysctl_size [8]byte | |||||
_sysctl_func [8]byte | |||||
_sysctl_parent [8]byte | |||||
_sysctl_desc [8]byte | |||||
} | |||||
func sysctlNodes(mib []int32) ([]sysctlNode, error) { | |||||
var olen uintptr | |||||
// Get a list of all sysctl nodes below the given MIB by performing | |||||
// a sysctl for the given MIB with CTL_QUERY appended. | |||||
mib = append(mib, _CTL_QUERY) | |||||
qnode := sysctlNode{Flags: _SYSCTL_VERS_1} | |||||
qp := (*byte)(unsafe.Pointer(&qnode)) | |||||
sz := unsafe.Sizeof(qnode) | |||||
if err := sysctl(mib, nil, &olen, qp, sz); err != nil { | |||||
return nil, err | |||||
} | |||||
// Now that we know the size, get the actual nodes. | |||||
nodes := make([]sysctlNode, olen/sz) | |||||
np := (*byte)(unsafe.Pointer(&nodes[0])) | |||||
if err := sysctl(mib, np, &olen, qp, sz); err != nil { | |||||
return nil, err | |||||
} | |||||
return nodes, nil | |||||
} | |||||
func nametomib(name string) ([]int32, error) { | |||||
// Split name into components. | |||||
var parts []string | |||||
last := 0 | |||||
for i := 0; i < len(name); i++ { | |||||
if name[i] == '.' { | |||||
parts = append(parts, name[last:i]) | |||||
last = i + 1 | |||||
} | |||||
} | |||||
parts = append(parts, name[last:]) | |||||
mib := []int32{} | |||||
// Discover the nodes and construct the MIB OID. | |||||
for partno, part := range parts { | |||||
nodes, err := sysctlNodes(mib) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
for _, node := range nodes { | |||||
n := make([]byte, 0) | |||||
for i := range node.Name { | |||||
if node.Name[i] != 0 { | |||||
n = append(n, byte(node.Name[i])) | |||||
} | |||||
} | |||||
if string(n) == part { | |||||
mib = append(mib, int32(node.Num)) | |||||
break | |||||
} | |||||
} | |||||
if len(mib) != partno+1 { | |||||
return nil, err | |||||
} | |||||
} | |||||
return mib, nil | |||||
} | |||||
// aarch64SysctlCPUID is struct aarch64_sysctl_cpu_id from NetBSD's <aarch64/armreg.h> | |||||
type aarch64SysctlCPUID struct { | |||||
midr uint64 /* Main ID Register */ | |||||
revidr uint64 /* Revision ID Register */ | |||||
mpidr uint64 /* Multiprocessor Affinity Register */ | |||||
aa64dfr0 uint64 /* A64 Debug Feature Register 0 */ | |||||
aa64dfr1 uint64 /* A64 Debug Feature Register 1 */ | |||||
aa64isar0 uint64 /* A64 Instruction Set Attribute Register 0 */ | |||||
aa64isar1 uint64 /* A64 Instruction Set Attribute Register 1 */ | |||||
aa64mmfr0 uint64 /* A64 Memory Model Feature Register 0 */ | |||||
aa64mmfr1 uint64 /* A64 Memory Model Feature Register 1 */ | |||||
aa64mmfr2 uint64 /* A64 Memory Model Feature Register 2 */ | |||||
aa64pfr0 uint64 /* A64 Processor Feature Register 0 */ | |||||
aa64pfr1 uint64 /* A64 Processor Feature Register 1 */ | |||||
aa64zfr0 uint64 /* A64 SVE Feature ID Register 0 */ | |||||
mvfr0 uint32 /* Media and VFP Feature Register 0 */ | |||||
mvfr1 uint32 /* Media and VFP Feature Register 1 */ | |||||
mvfr2 uint32 /* Media and VFP Feature Register 2 */ | |||||
pad uint32 | |||||
clidr uint64 /* Cache Level ID Register */ | |||||
ctr uint64 /* Cache Type Register */ | |||||
} | |||||
func sysctlCPUID(name string) (*aarch64SysctlCPUID, error) { | |||||
mib, err := nametomib(name) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
out := aarch64SysctlCPUID{} | |||||
n := unsafe.Sizeof(out) | |||||
_, _, errno := syscall.Syscall6( | |||||
syscall.SYS___SYSCTL, | |||||
uintptr(unsafe.Pointer(&mib[0])), | |||||
uintptr(len(mib)), | |||||
uintptr(unsafe.Pointer(&out)), | |||||
uintptr(unsafe.Pointer(&n)), | |||||
uintptr(0), | |||||
uintptr(0)) | |||||
if errno != 0 { | |||||
return nil, errno | |||||
} | |||||
return &out, nil | |||||
} | |||||
func doinit() { | |||||
cpuid, err := sysctlCPUID("machdep.cpu0.cpu_id") | |||||
if err != nil { | |||||
setMinimalFeatures() | |||||
return | |||||
} | |||||
parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64pfr0) | |||||
Initialized = true | |||||
} |
@@ -0,0 +1,10 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build !linux && arm | |||||
// +build !linux,arm | |||||
package cpu | |||||
func archInit() {} |
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !linux,arm64 | |||||
//go:build !linux && !netbsd && arm64 | |||||
// +build !linux,!netbsd,arm64 | |||||
package cpu | package cpu | ||||
@@ -0,0 +1,13 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build !linux && (mips64 || mips64le) | |||||
// +build !linux | |||||
// +build mips64 mips64le | |||||
package cpu | |||||
func archInit() { | |||||
Initialized = true | |||||
} |
@@ -0,0 +1,17 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build ppc64 || ppc64le | |||||
// +build ppc64 ppc64le | |||||
package cpu | |||||
const cacheLineSize = 128 | |||||
func initOptions() { | |||||
options = []option{ | |||||
{Name: "darn", Feature: &PPC64.HasDARN}, | |||||
{Name: "scv", Feature: &PPC64.HasSCV}, | |||||
} | |||||
} |
@@ -2,8 +2,11 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build riscv64 | |||||
// +build riscv64 | // +build riscv64 | ||||
package cpu | package cpu | ||||
const cacheLineSize = 32 | const cacheLineSize = 32 | ||||
func initOptions() {} |
@@ -0,0 +1,172 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
package cpu | |||||
const cacheLineSize = 256 | |||||
func initOptions() { | |||||
options = []option{ | |||||
{Name: "zarch", Feature: &S390X.HasZARCH, Required: true}, | |||||
{Name: "stfle", Feature: &S390X.HasSTFLE, Required: true}, | |||||
{Name: "ldisp", Feature: &S390X.HasLDISP, Required: true}, | |||||
{Name: "eimm", Feature: &S390X.HasEIMM, Required: true}, | |||||
{Name: "dfp", Feature: &S390X.HasDFP}, | |||||
{Name: "etf3eh", Feature: &S390X.HasETF3EH}, | |||||
{Name: "msa", Feature: &S390X.HasMSA}, | |||||
{Name: "aes", Feature: &S390X.HasAES}, | |||||
{Name: "aescbc", Feature: &S390X.HasAESCBC}, | |||||
{Name: "aesctr", Feature: &S390X.HasAESCTR}, | |||||
{Name: "aesgcm", Feature: &S390X.HasAESGCM}, | |||||
{Name: "ghash", Feature: &S390X.HasGHASH}, | |||||
{Name: "sha1", Feature: &S390X.HasSHA1}, | |||||
{Name: "sha256", Feature: &S390X.HasSHA256}, | |||||
{Name: "sha3", Feature: &S390X.HasSHA3}, | |||||
{Name: "sha512", Feature: &S390X.HasSHA512}, | |||||
{Name: "vx", Feature: &S390X.HasVX}, | |||||
{Name: "vxe", Feature: &S390X.HasVXE}, | |||||
} | |||||
} | |||||
// bitIsSet reports whether the bit at index is set. The bit index | |||||
// is in big endian order, so bit index 0 is the leftmost bit. | |||||
func bitIsSet(bits []uint64, index uint) bool { | |||||
return bits[index/64]&((1<<63)>>(index%64)) != 0 | |||||
} | |||||
// facility is a bit index for the named facility. | |||||
type facility uint8 | |||||
const ( | |||||
// mandatory facilities | |||||
zarch facility = 1 // z architecture mode is active | |||||
stflef facility = 7 // store-facility-list-extended | |||||
ldisp facility = 18 // long-displacement | |||||
eimm facility = 21 // extended-immediate | |||||
// miscellaneous facilities | |||||
dfp facility = 42 // decimal-floating-point | |||||
etf3eh facility = 30 // extended-translation 3 enhancement | |||||
// cryptography facilities | |||||
msa facility = 17 // message-security-assist | |||||
msa3 facility = 76 // message-security-assist extension 3 | |||||
msa4 facility = 77 // message-security-assist extension 4 | |||||
msa5 facility = 57 // message-security-assist extension 5 | |||||
msa8 facility = 146 // message-security-assist extension 8 | |||||
msa9 facility = 155 // message-security-assist extension 9 | |||||
// vector facilities | |||||
vx facility = 129 // vector facility | |||||
vxe facility = 135 // vector-enhancements 1 | |||||
vxe2 facility = 148 // vector-enhancements 2 | |||||
) | |||||
// facilityList contains the result of an STFLE call. | |||||
// Bits are numbered in big endian order so the | |||||
// leftmost bit (the MSB) is at index 0. | |||||
type facilityList struct { | |||||
bits [4]uint64 | |||||
} | |||||
// Has reports whether the given facilities are present. | |||||
func (s *facilityList) Has(fs ...facility) bool { | |||||
if len(fs) == 0 { | |||||
panic("no facility bits provided") | |||||
} | |||||
for _, f := range fs { | |||||
if !bitIsSet(s.bits[:], uint(f)) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
// function is the code for the named cryptographic function. | |||||
type function uint8 | |||||
const ( | |||||
// KM{,A,C,CTR} function codes | |||||
aes128 function = 18 // AES-128 | |||||
aes192 function = 19 // AES-192 | |||||
aes256 function = 20 // AES-256 | |||||
// K{I,L}MD function codes | |||||
sha1 function = 1 // SHA-1 | |||||
sha256 function = 2 // SHA-256 | |||||
sha512 function = 3 // SHA-512 | |||||
sha3_224 function = 32 // SHA3-224 | |||||
sha3_256 function = 33 // SHA3-256 | |||||
sha3_384 function = 34 // SHA3-384 | |||||
sha3_512 function = 35 // SHA3-512 | |||||
shake128 function = 36 // SHAKE-128 | |||||
shake256 function = 37 // SHAKE-256 | |||||
// KLMD function codes | |||||
ghash function = 65 // GHASH | |||||
) | |||||
// queryResult contains the result of a Query function | |||||
// call. Bits are numbered in big endian order so the | |||||
// leftmost bit (the MSB) is at index 0. | |||||
type queryResult struct { | |||||
bits [2]uint64 | |||||
} | |||||
// Has reports whether the given functions are present. | |||||
func (q *queryResult) Has(fns ...function) bool { | |||||
if len(fns) == 0 { | |||||
panic("no function codes provided") | |||||
} | |||||
for _, f := range fns { | |||||
if !bitIsSet(q.bits[:], uint(f)) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
func doinit() { | |||||
initS390Xbase() | |||||
// We need implementations of stfle, km and so on | |||||
// to detect cryptographic features. | |||||
if !haveAsmFunctions() { | |||||
return | |||||
} | |||||
// optional cryptographic functions | |||||
if S390X.HasMSA { | |||||
aes := []function{aes128, aes192, aes256} | |||||
// cipher message | |||||
km, kmc := kmQuery(), kmcQuery() | |||||
S390X.HasAES = km.Has(aes...) | |||||
S390X.HasAESCBC = kmc.Has(aes...) | |||||
if S390X.HasSTFLE { | |||||
facilities := stfle() | |||||
if facilities.Has(msa4) { | |||||
kmctr := kmctrQuery() | |||||
S390X.HasAESCTR = kmctr.Has(aes...) | |||||
} | |||||
if facilities.Has(msa8) { | |||||
kma := kmaQuery() | |||||
S390X.HasAESGCM = kma.Has(aes...) | |||||
} | |||||
} | |||||
// compute message digest | |||||
kimd := kimdQuery() // intermediate (no padding) | |||||
klmd := klmdQuery() // last (padding) | |||||
S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) | |||||
S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) | |||||
S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) | |||||
S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist | |||||
sha3 := []function{ | |||||
sha3_224, sha3_256, sha3_384, sha3_512, | |||||
shake128, shake256, | |||||
} | |||||
S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...) | |||||
} | |||||
} |
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build wasm | |||||
// +build wasm | // +build wasm | ||||
package cpu | package cpu | ||||
@@ -11,3 +12,7 @@ package cpu | |||||
// rules are good enough. | // rules are good enough. | ||||
const cacheLineSize = 0 | const cacheLineSize = 0 | ||||
func initOptions() {} | |||||
func archInit() {} |
@@ -2,13 +2,62 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build 386 || amd64 || amd64p32 | |||||
// +build 386 amd64 amd64p32 | // +build 386 amd64 amd64p32 | ||||
package cpu | package cpu | ||||
import "runtime" | |||||
const cacheLineSize = 64 | const cacheLineSize = 64 | ||||
func init() { | |||||
func initOptions() { | |||||
options = []option{ | |||||
{Name: "adx", Feature: &X86.HasADX}, | |||||
{Name: "aes", Feature: &X86.HasAES}, | |||||
{Name: "avx", Feature: &X86.HasAVX}, | |||||
{Name: "avx2", Feature: &X86.HasAVX2}, | |||||
{Name: "avx512", Feature: &X86.HasAVX512}, | |||||
{Name: "avx512f", Feature: &X86.HasAVX512F}, | |||||
{Name: "avx512cd", Feature: &X86.HasAVX512CD}, | |||||
{Name: "avx512er", Feature: &X86.HasAVX512ER}, | |||||
{Name: "avx512pf", Feature: &X86.HasAVX512PF}, | |||||
{Name: "avx512vl", Feature: &X86.HasAVX512VL}, | |||||
{Name: "avx512bw", Feature: &X86.HasAVX512BW}, | |||||
{Name: "avx512dq", Feature: &X86.HasAVX512DQ}, | |||||
{Name: "avx512ifma", Feature: &X86.HasAVX512IFMA}, | |||||
{Name: "avx512vbmi", Feature: &X86.HasAVX512VBMI}, | |||||
{Name: "avx512vnniw", Feature: &X86.HasAVX5124VNNIW}, | |||||
{Name: "avx5124fmaps", Feature: &X86.HasAVX5124FMAPS}, | |||||
{Name: "avx512vpopcntdq", Feature: &X86.HasAVX512VPOPCNTDQ}, | |||||
{Name: "avx512vpclmulqdq", Feature: &X86.HasAVX512VPCLMULQDQ}, | |||||
{Name: "avx512vnni", Feature: &X86.HasAVX512VNNI}, | |||||
{Name: "avx512gfni", Feature: &X86.HasAVX512GFNI}, | |||||
{Name: "avx512vaes", Feature: &X86.HasAVX512VAES}, | |||||
{Name: "avx512vbmi2", Feature: &X86.HasAVX512VBMI2}, | |||||
{Name: "avx512bitalg", Feature: &X86.HasAVX512BITALG}, | |||||
{Name: "avx512bf16", Feature: &X86.HasAVX512BF16}, | |||||
{Name: "bmi1", Feature: &X86.HasBMI1}, | |||||
{Name: "bmi2", Feature: &X86.HasBMI2}, | |||||
{Name: "erms", Feature: &X86.HasERMS}, | |||||
{Name: "fma", Feature: &X86.HasFMA}, | |||||
{Name: "osxsave", Feature: &X86.HasOSXSAVE}, | |||||
{Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ}, | |||||
{Name: "popcnt", Feature: &X86.HasPOPCNT}, | |||||
{Name: "rdrand", Feature: &X86.HasRDRAND}, | |||||
{Name: "rdseed", Feature: &X86.HasRDSEED}, | |||||
{Name: "sse3", Feature: &X86.HasSSE3}, | |||||
{Name: "sse41", Feature: &X86.HasSSE41}, | |||||
{Name: "sse42", Feature: &X86.HasSSE42}, | |||||
{Name: "ssse3", Feature: &X86.HasSSSE3}, | |||||
// These capabilities should always be enabled on amd64: | |||||
{Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"}, | |||||
} | |||||
} | |||||
func archInit() { | |||||
Initialized = true | Initialized = true | ||||
maxID, _, _, _ := cpuid(0, 0) | maxID, _, _, _ := cpuid(0, 0) | ||||
@@ -31,12 +80,21 @@ func init() { | |||||
X86.HasOSXSAVE = isSet(27, ecx1) | X86.HasOSXSAVE = isSet(27, ecx1) | ||||
X86.HasRDRAND = isSet(30, ecx1) | X86.HasRDRAND = isSet(30, ecx1) | ||||
osSupportsAVX := false | |||||
var osSupportsAVX, osSupportsAVX512 bool | |||||
// For XGETBV, OSXSAVE bit is required and sufficient. | // For XGETBV, OSXSAVE bit is required and sufficient. | ||||
if X86.HasOSXSAVE { | if X86.HasOSXSAVE { | ||||
eax, _ := xgetbv() | eax, _ := xgetbv() | ||||
// Check if XMM and YMM registers have OS support. | // Check if XMM and YMM registers have OS support. | ||||
osSupportsAVX = isSet(1, eax) && isSet(2, eax) | osSupportsAVX = isSet(1, eax) && isSet(2, eax) | ||||
if runtime.GOOS == "darwin" { | |||||
// Check darwin commpage for AVX512 support. Necessary because: | |||||
// https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/osfmk/i386/fpu.c#L175-L201 | |||||
osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512() | |||||
} else { | |||||
// Check if OPMASK and ZMM registers have OS support. | |||||
osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax) | |||||
} | |||||
} | } | ||||
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX | X86.HasAVX = isSet(28, ecx1) && osSupportsAVX | ||||
@@ -45,13 +103,38 @@ func init() { | |||||
return | return | ||||
} | } | ||||
_, ebx7, _, _ := cpuid(7, 0) | |||||
_, ebx7, ecx7, edx7 := cpuid(7, 0) | |||||
X86.HasBMI1 = isSet(3, ebx7) | X86.HasBMI1 = isSet(3, ebx7) | ||||
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX | X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX | ||||
X86.HasBMI2 = isSet(8, ebx7) | X86.HasBMI2 = isSet(8, ebx7) | ||||
X86.HasERMS = isSet(9, ebx7) | X86.HasERMS = isSet(9, ebx7) | ||||
X86.HasRDSEED = isSet(18, ebx7) | X86.HasRDSEED = isSet(18, ebx7) | ||||
X86.HasADX = isSet(19, ebx7) | X86.HasADX = isSet(19, ebx7) | ||||
X86.HasAVX512 = isSet(16, ebx7) && osSupportsAVX512 // Because avx-512 foundation is the core required extension | |||||
if X86.HasAVX512 { | |||||
X86.HasAVX512F = true | |||||
X86.HasAVX512CD = isSet(28, ebx7) | |||||
X86.HasAVX512ER = isSet(27, ebx7) | |||||
X86.HasAVX512PF = isSet(26, ebx7) | |||||
X86.HasAVX512VL = isSet(31, ebx7) | |||||
X86.HasAVX512BW = isSet(30, ebx7) | |||||
X86.HasAVX512DQ = isSet(17, ebx7) | |||||
X86.HasAVX512IFMA = isSet(21, ebx7) | |||||
X86.HasAVX512VBMI = isSet(1, ecx7) | |||||
X86.HasAVX5124VNNIW = isSet(2, edx7) | |||||
X86.HasAVX5124FMAPS = isSet(3, edx7) | |||||
X86.HasAVX512VPOPCNTDQ = isSet(14, ecx7) | |||||
X86.HasAVX512VPCLMULQDQ = isSet(10, ecx7) | |||||
X86.HasAVX512VNNI = isSet(11, ecx7) | |||||
X86.HasAVX512GFNI = isSet(8, ecx7) | |||||
X86.HasAVX512VAES = isSet(9, ecx7) | |||||
X86.HasAVX512VBMI2 = isSet(6, ecx7) | |||||
X86.HasAVX512BITALG = isSet(12, ecx7) | |||||
eax71, _, _, _ := cpuid(7, 1) | |||||
X86.HasAVX512BF16 = isSet(5, eax71) | |||||
} | |||||
} | } | ||||
func isSet(bitpos uint, value uint32) bool { | func isSet(bitpos uint, value uint32) bool { | ||||
@@ -2,8 +2,9 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build (386 || amd64 || amd64p32) && gc | |||||
// +build 386 amd64 amd64p32 | // +build 386 amd64 amd64p32 | ||||
// +build !gccgo | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -25,3 +26,27 @@ TEXT ·xgetbv(SB),NOSPLIT,$0-8 | |||||
MOVL AX, eax+0(FP) | MOVL AX, eax+0(FP) | ||||
MOVL DX, edx+4(FP) | MOVL DX, edx+4(FP) | ||||
RET | RET | ||||
// func darwinSupportsAVX512() bool | |||||
TEXT ·darwinSupportsAVX512(SB), NOSPLIT, $0-1 | |||||
MOVB $0, ret+0(FP) // default to false | |||||
#ifdef GOOS_darwin // return if not darwin | |||||
#ifdef GOARCH_amd64 // return if not amd64 | |||||
// These values from: | |||||
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h | |||||
#define commpage64_base_address 0x00007fffffe00000 | |||||
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010) | |||||
#define commpage64_version (commpage64_base_address+0x01E) | |||||
#define hasAVX512F 0x0000004000000000 | |||||
MOVQ $commpage64_version, BX | |||||
CMPW (BX), $13 // cpu_capabilities64 undefined in versions < 13 | |||||
JL no_avx512 | |||||
MOVQ $commpage64_cpu_capabilities64, BX | |||||
MOVQ $hasAVX512F, CX | |||||
TESTQ (BX), CX | |||||
JZ no_avx512 | |||||
MOVB $1, ret+0(FP) | |||||
no_avx512: | |||||
#endif | |||||
#endif | |||||
RET |
@@ -0,0 +1,10 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
package cpu | |||||
func archInit() { | |||||
doinit() | |||||
Initialized = true | |||||
} |
@@ -0,0 +1,25 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
package cpu | |||||
func initS390Xbase() { | |||||
// get the facilities list | |||||
facilities := stfle() | |||||
// mandatory | |||||
S390X.HasZARCH = facilities.Has(zarch) | |||||
S390X.HasSTFLE = facilities.Has(stflef) | |||||
S390X.HasLDISP = facilities.Has(ldisp) | |||||
S390X.HasEIMM = facilities.Has(eimm) | |||||
// optional | |||||
S390X.HasETF3EH = facilities.Has(etf3eh) | |||||
S390X.HasDFP = facilities.Has(dfp) | |||||
S390X.HasMSA = facilities.Has(msa) | |||||
S390X.HasVX = facilities.Has(vx) | |||||
if S390X.HasVX { | |||||
S390X.HasVXE = facilities.Has(vxe) | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// Recreate a getsystemcfg syscall handler instead of | |||||
// using the one provided by x/sys/unix to avoid having | |||||
// the dependency between them. (See golang.org/issue/32102) | |||||
// Morever, this file will be used during the building of | |||||
// gccgo's libgo and thus must not used a CGo method. | |||||
//go:build aix && gccgo | |||||
// +build aix,gccgo | |||||
package cpu | |||||
import ( | |||||
"syscall" | |||||
) | |||||
//extern getsystemcfg | |||||
func gccgoGetsystemcfg(label uint32) (r uint64) | |||||
func callgetsystemcfg(label int) (r1 uintptr, e1 syscall.Errno) { | |||||
r1 = uintptr(gccgoGetsystemcfg(uint32(label))) | |||||
e1 = syscall.GetErrno() | |||||
return | |||||
} |
@@ -6,8 +6,8 @@ | |||||
// system call on AIX without depending on x/sys/unix. | // system call on AIX without depending on x/sys/unix. | ||||
// (See golang.org/issue/32102) | // (See golang.org/issue/32102) | ||||
// +build aix,ppc64 | |||||
// +build !gccgo | |||||
//go:build aix && ppc64 && gc | |||||
// +build aix,ppc64,gc | |||||
package cpu | package cpu | ||||
@@ -0,0 +1,102 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// Package execabs is a drop-in replacement for os/exec | |||||
// that requires PATH lookups to find absolute paths. | |||||
// That is, execabs.Command("cmd") runs the same PATH lookup | |||||
// as exec.Command("cmd"), but if the result is a path | |||||
// which is relative, the Run and Start methods will report | |||||
// an error instead of running the executable. | |||||
// | |||||
// See https://blog.golang.org/path-security for more information | |||||
// about when it may be necessary or appropriate to use this package. | |||||
package execabs | |||||
import ( | |||||
"context" | |||||
"fmt" | |||||
"os/exec" | |||||
"path/filepath" | |||||
"reflect" | |||||
"unsafe" | |||||
) | |||||
// ErrNotFound is the error resulting if a path search failed to find an executable file. | |||||
// It is an alias for exec.ErrNotFound. | |||||
var ErrNotFound = exec.ErrNotFound | |||||
// Cmd represents an external command being prepared or run. | |||||
// It is an alias for exec.Cmd. | |||||
type Cmd = exec.Cmd | |||||
// Error is returned by LookPath when it fails to classify a file as an executable. | |||||
// It is an alias for exec.Error. | |||||
type Error = exec.Error | |||||
// An ExitError reports an unsuccessful exit by a command. | |||||
// It is an alias for exec.ExitError. | |||||
type ExitError = exec.ExitError | |||||
func relError(file, path string) error { | |||||
return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path) | |||||
} | |||||
// LookPath searches for an executable named file in the directories | |||||
// named by the PATH environment variable. If file contains a slash, | |||||
// it is tried directly and the PATH is not consulted. The result will be | |||||
// an absolute path. | |||||
// | |||||
// LookPath differs from exec.LookPath in its handling of PATH lookups, | |||||
// which are used for file names without slashes. If exec.LookPath's | |||||
// PATH lookup would have returned an executable from the current directory, | |||||
// LookPath instead returns an error. | |||||
func LookPath(file string) (string, error) { | |||||
path, err := exec.LookPath(file) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
if filepath.Base(file) == file && !filepath.IsAbs(path) { | |||||
return "", relError(file, path) | |||||
} | |||||
return path, nil | |||||
} | |||||
func fixCmd(name string, cmd *exec.Cmd) { | |||||
if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) { | |||||
// exec.Command was called with a bare binary name and | |||||
// exec.LookPath returned a path which is not absolute. | |||||
// Set cmd.lookPathErr and clear cmd.Path so that it | |||||
// cannot be run. | |||||
lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer())) | |||||
if *lookPathErr == nil { | |||||
*lookPathErr = relError(name, cmd.Path) | |||||
} | |||||
cmd.Path = "" | |||||
} | |||||
} | |||||
// CommandContext is like Command but includes a context. | |||||
// | |||||
// The provided context is used to kill the process (by calling os.Process.Kill) | |||||
// if the context becomes done before the command completes on its own. | |||||
func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd { | |||||
cmd := exec.CommandContext(ctx, name, arg...) | |||||
fixCmd(name, cmd) | |||||
return cmd | |||||
} | |||||
// Command returns the Cmd struct to execute the named program with the given arguments. | |||||
// See exec.Command for most details. | |||||
// | |||||
// Command differs from exec.Command in its handling of PATH lookups, | |||||
// which are used when the program name contains no slashes. | |||||
// If exec.Command would have returned an exec.Cmd configured to run an | |||||
// executable from the current directory, Command instead | |||||
// returns an exec.Cmd that will return an error from Start or Run. | |||||
func Command(name string, arg ...string) *exec.Cmd { | |||||
cmd := exec.Command(name, arg...) | |||||
fixCmd(name, cmd) | |||||
return cmd | |||||
} |
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||||
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9 | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||||
// +build go1.9 | // +build go1.9 | ||||
package unix | package unix | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -1,14 +1,14 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Copyright 2021 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc | |||||
// +build darwin freebsd netbsd openbsd | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
// | |||||
// System call support for 386, NetBSD | |||||
// | |||||
// System call support for 386 BSD | |||||
// Just jump to package syscall's implementation for all these functions. | // Just jump to package syscall's implementation for all these functions. | ||||
// The runtime may know about them. | // The runtime may know about them. | ||||
@@ -22,7 +22,7 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | TEXT ·Syscall9(SB),NOSPLIT,$0-52 | ||||
JMP syscall·Syscall9(SB) | JMP syscall·Syscall9(SB) | ||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·RawSyscall(SB) | JMP syscall·RawSyscall(SB) | ||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
@@ -1,14 +1,14 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Copyright 2021 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc | |||||
// +build darwin dragonfly freebsd netbsd openbsd | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
// | |||||
// System call support for AMD64, OpenBSD | |||||
// | |||||
// System call support for AMD64 BSD | |||||
// Just jump to package syscall's implementation for all these functions. | // Just jump to package syscall's implementation for all these functions. | ||||
// The runtime may know about them. | // The runtime may know about them. |
@@ -1,15 +1,14 @@ | |||||
// Copyright 2015 The Go Authors. All rights reserved. | |||||
// Copyright 2021 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
// +build arm,darwin | |||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc | |||||
// +build darwin freebsd netbsd openbsd | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
// | |||||
// System call support for ARM, Darwin | |||||
// | |||||
// System call support for ARM BSD | |||||
// Just jump to package syscall's implementation for all these functions. | // Just jump to package syscall's implementation for all these functions. | ||||
// The runtime may know about them. | // The runtime may know about them. |
@@ -1,14 +1,14 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Copyright 2021 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc | |||||
// +build darwin freebsd netbsd openbsd | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
// | |||||
// System call support for AMD64, NetBSD | |||||
// | |||||
// System call support for ARM64 BSD | |||||
// Just jump to package syscall's implementation for all these functions. | // Just jump to package syscall's implementation for all these functions. | ||||
// The runtime may know about them. | // The runtime may know about them. |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for 386, Darwin | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for AMD64, Darwin | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -1,30 +0,0 @@ | |||||
// Copyright 2015 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
// +build arm64,darwin | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for AMD64, Darwin | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||||
B syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||||
B syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||||
B syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||||
B syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||||
B syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for AMD64, DragonFly | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for 386, FreeBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for AMD64, FreeBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2012 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for ARM, FreeBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||||
B syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
B syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||||
B syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
B syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||||
B syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2018 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for ARM64, FreeBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,9 +2,10 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && arm64 && gc | |||||
// +build linux | // +build linux | ||||
// +build arm64 | // +build arm64 | ||||
// +build !gccgo | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,9 +2,10 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && (mips64 || mips64le) && gc | |||||
// +build linux | // +build linux | ||||
// +build mips64 mips64le | // +build mips64 mips64le | ||||
// +build !gccgo | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,9 +2,10 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && (mips || mipsle) && gc | |||||
// +build linux | // +build linux | ||||
// +build mips mipsle | // +build mips mipsle | ||||
// +build !gccgo | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,9 +2,10 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build linux && (ppc64 || ppc64le) && gc | |||||
// +build linux | // +build linux | ||||
// +build ppc64 ppc64le | // +build ppc64 ppc64le | ||||
// +build !gccgo | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,7 +2,9 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build riscv64,!gccgo | |||||
//go:build riscv64 && gc | |||||
// +build riscv64 | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -2,9 +2,10 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build s390x | |||||
//go:build linux && s390x && gc | |||||
// +build linux | // +build linux | ||||
// +build !gccgo | |||||
// +build s390x | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -1,29 +0,0 @@ | |||||
// Copyright 2013 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for ARM, NetBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||||
B syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
B syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||||
B syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
B syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||||
B syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2019 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for ARM64, NetBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||||
B syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||||
B syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||||
B syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||||
B syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||||
B syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2009 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for 386, OpenBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
JMP syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||||
JMP syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
JMP syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||||
JMP syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||||
// Copyright 2017 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build !gccgo | |||||
#include "textflag.h" | |||||
// | |||||
// System call support for ARM, OpenBSD | |||||
// | |||||
// Just jump to package syscall's implementation for all these functions. | |||||
// The runtime may know about them. | |||||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||||
B syscall·Syscall(SB) | |||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||||
B syscall·Syscall6(SB) | |||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||||
B syscall·Syscall9(SB) | |||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||||
B syscall·RawSyscall(SB) | |||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||||
B syscall·RawSyscall6(SB) |
@@ -2,12 +2,13 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
// | // | ||||
// System call support for arm64, OpenBSD | |||||
// System call support for mips64, OpenBSD | |||||
// | // | ||||
// Just jump to package syscall's implementation for all these functions. | // Just jump to package syscall's implementation for all these functions. |
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !gccgo | |||||
//go:build gc | |||||
// +build gc | |||||
#include "textflag.h" | #include "textflag.h" | ||||
@@ -0,0 +1,426 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build zos && s390x && gc | |||||
// +build zos | |||||
// +build s390x | |||||
// +build gc | |||||
#include "textflag.h" | |||||
#define PSALAA 1208(R0) | |||||
#define GTAB64(x) 80(x) | |||||
#define LCA64(x) 88(x) | |||||
#define CAA(x) 8(x) | |||||
#define EDCHPXV(x) 1016(x) // in the CAA | |||||
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA | |||||
// SS_*, where x=SAVSTACK_ASYNC | |||||
#define SS_LE(x) 0(x) | |||||
#define SS_GO(x) 8(x) | |||||
#define SS_ERRNO(x) 16(x) | |||||
#define SS_ERRNOJR(x) 20(x) | |||||
#define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6 | |||||
TEXT ·clearErrno(SB),NOSPLIT,$0-0 | |||||
BL addrerrno<>(SB) | |||||
MOVD $0, 0(R3) | |||||
RET | |||||
// Returns the address of errno in R3. | |||||
TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get __errno FuncDesc. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
ADD $(0x156*16), R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Switch to saved LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Call __errno function. | |||||
LE_CALL | |||||
NOPH | |||||
// Switch back to Go stack. | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
RET | |||||
TEXT ·syscall_syscall(SB),NOSPLIT,$0-56 | |||||
BL runtime·entersyscall(SB) | |||||
MOVD a1+8(FP), R1 | |||||
MOVD a2+16(FP), R2 | |||||
MOVD a3+24(FP), R3 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get function. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
MOVD trap+0(FP), R5 | |||||
SLD $4, R5 | |||||
ADD R5, R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Restore LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Call function. | |||||
LE_CALL | |||||
NOPH | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
MOVD R3, r1+32(FP) | |||||
MOVD R0, r2+40(FP) | |||||
MOVD R0, err+48(FP) | |||||
MOVW R3, R4 | |||||
CMP R4, $-1 | |||||
BNE done | |||||
BL addrerrno<>(SB) | |||||
MOVWZ 0(R3), R3 | |||||
MOVD R3, err+48(FP) | |||||
done: | |||||
BL runtime·exitsyscall(SB) | |||||
RET | |||||
TEXT ·syscall_rawsyscall(SB),NOSPLIT,$0-56 | |||||
MOVD a1+8(FP), R1 | |||||
MOVD a2+16(FP), R2 | |||||
MOVD a3+24(FP), R3 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get function. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
MOVD trap+0(FP), R5 | |||||
SLD $4, R5 | |||||
ADD R5, R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Restore LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Call function. | |||||
LE_CALL | |||||
NOPH | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
MOVD R3, r1+32(FP) | |||||
MOVD R0, r2+40(FP) | |||||
MOVD R0, err+48(FP) | |||||
MOVW R3, R4 | |||||
CMP R4, $-1 | |||||
BNE done | |||||
BL addrerrno<>(SB) | |||||
MOVWZ 0(R3), R3 | |||||
MOVD R3, err+48(FP) | |||||
done: | |||||
RET | |||||
TEXT ·syscall_syscall6(SB),NOSPLIT,$0-80 | |||||
BL runtime·entersyscall(SB) | |||||
MOVD a1+8(FP), R1 | |||||
MOVD a2+16(FP), R2 | |||||
MOVD a3+24(FP), R3 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get function. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
MOVD trap+0(FP), R5 | |||||
SLD $4, R5 | |||||
ADD R5, R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Restore LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Fill in parameter list. | |||||
MOVD a4+32(FP), R12 | |||||
MOVD R12, (2176+24)(R4) | |||||
MOVD a5+40(FP), R12 | |||||
MOVD R12, (2176+32)(R4) | |||||
MOVD a6+48(FP), R12 | |||||
MOVD R12, (2176+40)(R4) | |||||
// Call function. | |||||
LE_CALL | |||||
NOPH | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
MOVD R3, r1+56(FP) | |||||
MOVD R0, r2+64(FP) | |||||
MOVD R0, err+72(FP) | |||||
MOVW R3, R4 | |||||
CMP R4, $-1 | |||||
BNE done | |||||
BL addrerrno<>(SB) | |||||
MOVWZ 0(R3), R3 | |||||
MOVD R3, err+72(FP) | |||||
done: | |||||
BL runtime·exitsyscall(SB) | |||||
RET | |||||
TEXT ·syscall_rawsyscall6(SB),NOSPLIT,$0-80 | |||||
MOVD a1+8(FP), R1 | |||||
MOVD a2+16(FP), R2 | |||||
MOVD a3+24(FP), R3 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get function. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
MOVD trap+0(FP), R5 | |||||
SLD $4, R5 | |||||
ADD R5, R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Restore LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Fill in parameter list. | |||||
MOVD a4+32(FP), R12 | |||||
MOVD R12, (2176+24)(R4) | |||||
MOVD a5+40(FP), R12 | |||||
MOVD R12, (2176+32)(R4) | |||||
MOVD a6+48(FP), R12 | |||||
MOVD R12, (2176+40)(R4) | |||||
// Call function. | |||||
LE_CALL | |||||
NOPH | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
MOVD R3, r1+56(FP) | |||||
MOVD R0, r2+64(FP) | |||||
MOVD R0, err+72(FP) | |||||
MOVW R3, R4 | |||||
CMP R4, $-1 | |||||
BNE done | |||||
BL ·rrno<>(SB) | |||||
MOVWZ 0(R3), R3 | |||||
MOVD R3, err+72(FP) | |||||
done: | |||||
RET | |||||
TEXT ·syscall_syscall9(SB),NOSPLIT,$0 | |||||
BL runtime·entersyscall(SB) | |||||
MOVD a1+8(FP), R1 | |||||
MOVD a2+16(FP), R2 | |||||
MOVD a3+24(FP), R3 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get function. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
MOVD trap+0(FP), R5 | |||||
SLD $4, R5 | |||||
ADD R5, R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Restore LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Fill in parameter list. | |||||
MOVD a4+32(FP), R12 | |||||
MOVD R12, (2176+24)(R4) | |||||
MOVD a5+40(FP), R12 | |||||
MOVD R12, (2176+32)(R4) | |||||
MOVD a6+48(FP), R12 | |||||
MOVD R12, (2176+40)(R4) | |||||
MOVD a7+56(FP), R12 | |||||
MOVD R12, (2176+48)(R4) | |||||
MOVD a8+64(FP), R12 | |||||
MOVD R12, (2176+56)(R4) | |||||
MOVD a9+72(FP), R12 | |||||
MOVD R12, (2176+64)(R4) | |||||
// Call function. | |||||
LE_CALL | |||||
NOPH | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
MOVD R3, r1+80(FP) | |||||
MOVD R0, r2+88(FP) | |||||
MOVD R0, err+96(FP) | |||||
MOVW R3, R4 | |||||
CMP R4, $-1 | |||||
BNE done | |||||
BL addrerrno<>(SB) | |||||
MOVWZ 0(R3), R3 | |||||
MOVD R3, err+96(FP) | |||||
done: | |||||
BL runtime·exitsyscall(SB) | |||||
RET | |||||
TEXT ·syscall_rawsyscall9(SB),NOSPLIT,$0 | |||||
MOVD a1+8(FP), R1 | |||||
MOVD a2+16(FP), R2 | |||||
MOVD a3+24(FP), R3 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get function. | |||||
MOVD CAA(R8), R9 | |||||
MOVD EDCHPXV(R9), R9 | |||||
MOVD trap+0(FP), R5 | |||||
SLD $4, R5 | |||||
ADD R5, R9 | |||||
LMG 0(R9), R5, R6 | |||||
// Restore LE stack. | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R4 | |||||
MOVD $0, 0(R9) | |||||
// Fill in parameter list. | |||||
MOVD a4+32(FP), R12 | |||||
MOVD R12, (2176+24)(R4) | |||||
MOVD a5+40(FP), R12 | |||||
MOVD R12, (2176+32)(R4) | |||||
MOVD a6+48(FP), R12 | |||||
MOVD R12, (2176+40)(R4) | |||||
MOVD a7+56(FP), R12 | |||||
MOVD R12, (2176+48)(R4) | |||||
MOVD a8+64(FP), R12 | |||||
MOVD R12, (2176+56)(R4) | |||||
MOVD a9+72(FP), R12 | |||||
MOVD R12, (2176+64)(R4) | |||||
// Call function. | |||||
LE_CALL | |||||
NOPH | |||||
XOR R0, R0 // Restore R0 to $0. | |||||
MOVD R4, 0(R9) // Save stack pointer. | |||||
MOVD R3, r1+80(FP) | |||||
MOVD R0, r2+88(FP) | |||||
MOVD R0, err+96(FP) | |||||
MOVW R3, R4 | |||||
CMP R4, $-1 | |||||
BNE done | |||||
BL addrerrno<>(SB) | |||||
MOVWZ 0(R3), R3 | |||||
MOVD R3, err+96(FP) | |||||
done: | |||||
RET | |||||
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64) | |||||
TEXT ·svcCall(SB),NOSPLIT,$0 | |||||
BL runtime·save_g(SB) // Save g and stack pointer | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD R15, 0(R9) | |||||
MOVD argv+8(FP), R1 // Move function arguments into registers | |||||
MOVD dsa+16(FP), g | |||||
MOVD fnptr+0(FP), R15 | |||||
BYTE $0x0D // Branch to function | |||||
BYTE $0xEF | |||||
BL runtime·load_g(SB) // Restore g and stack pointer | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
MOVD SAVSTACK_ASYNC(R8), R9 | |||||
MOVD 0(R9), R15 | |||||
RET | |||||
// func svcLoad(name *byte) unsafe.Pointer | |||||
TEXT ·svcLoad(SB),NOSPLIT,$0 | |||||
MOVD R15, R2 // Save go stack pointer | |||||
MOVD name+0(FP), R0 // Move SVC args into registers | |||||
MOVD $0x80000000, R1 | |||||
MOVD $0, R15 | |||||
BYTE $0x0A // SVC 08 LOAD | |||||
BYTE $0x08 | |||||
MOVW R15, R3 // Save return code from SVC | |||||
MOVD R2, R15 // Restore go stack pointer | |||||
CMP R3, $0 // Check SVC return code | |||||
BNE error | |||||
MOVD $-2, R3 // Reset last bit of entry point to zero | |||||
AND R0, R3 | |||||
MOVD R3, addr+8(FP) // Return entry point returned by SVC | |||||
CMP R0, R3 // Check if last bit of entry point was set | |||||
BNE done | |||||
MOVD R15, R2 // Save go stack pointer | |||||
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08) | |||||
BYTE $0x0A // SVC 09 DELETE | |||||
BYTE $0x09 | |||||
MOVD R2, R15 // Restore go stack pointer | |||||
error: | |||||
MOVD $0, addr+8(FP) // Return 0 on failure | |||||
done: | |||||
XOR R0, R0 // Reset r0 to 0 | |||||
RET | |||||
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64 | |||||
TEXT ·svcUnload(SB),NOSPLIT,$0 | |||||
MOVD R15, R2 // Save go stack pointer | |||||
MOVD name+0(FP), R0 // Move SVC args into registers | |||||
MOVD addr+8(FP), R15 | |||||
BYTE $0x0A // SVC 09 | |||||
BYTE $0x09 | |||||
XOR R0, R0 // Reset r0 to 0 | |||||
MOVD R15, R1 // Save SVC return code | |||||
MOVD R2, R15 // Restore go stack pointer | |||||
MOVD R1, rc+0(FP) // Return SVC return code | |||||
RET | |||||
// func gettid() uint64 | |||||
TEXT ·gettid(SB), NOSPLIT, $0 | |||||
// Get library control area (LCA). | |||||
MOVW PSALAA, R8 | |||||
MOVD LCA64(R8), R8 | |||||
// Get CEECAATHDID | |||||
MOVD CAA(R8), R9 | |||||
MOVD 0x3D0(R9), R9 | |||||
MOVD R9, ret+0(FP) | |||||
RET |
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build freebsd | |||||
// +build freebsd | // +build freebsd | ||||
package unix | package unix | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||||
package unix | package unix | ||||
@@ -2,8 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build aix | |||||
// +build ppc | |||||
//go:build aix && ppc | |||||
// +build aix,ppc | |||||
// Functions to access/create device major and minor numbers matching the | // Functions to access/create device major and minor numbers matching the | ||||
// encoding used by AIX. | // encoding used by AIX. | ||||
@@ -2,8 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build aix | |||||
// +build ppc64 | |||||
//go:build aix && ppc64 | |||||
// +build aix,ppc64 | |||||
// Functions to access/create device major and minor numbers matching the | // Functions to access/create device major and minor numbers matching the | ||||
// encoding used AIX. | // encoding used AIX. | ||||
@@ -0,0 +1,29 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build zos && s390x | |||||
// +build zos,s390x | |||||
// Functions to access/create device major and minor numbers matching the | |||||
// encoding used by z/OS. | |||||
// | |||||
// The information below is extracted and adapted from <sys/stat.h> macros. | |||||
package unix | |||||
// Major returns the major component of a z/OS device number. | |||||
func Major(dev uint64) uint32 { | |||||
return uint32((dev >> 16) & 0x0000FFFF) | |||||
} | |||||
// Minor returns the minor component of a z/OS device number. | |||||
func Minor(dev uint64) uint32 { | |||||
return uint32(dev & 0x0000FFFF) | |||||
} | |||||
// Mkdev returns a z/OS device number generated from the given major and minor | |||||
// components. | |||||
func Mkdev(major, minor uint32) uint64 { | |||||
return (uint64(major) << 16) | uint64(minor) | |||||
} |
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | ||||
package unix | package unix | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// | // | ||||
// +build ppc64 s390x mips mips64 | |||||
//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 | |||||
// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64 | |||||
package unix | package unix | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// | // | ||||
// +build 386 amd64 amd64p32 arm arm64 ppc64le mipsle mips64le riscv64 | |||||
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh | |||||
// +build 386 amd64 amd64p32 alpha arm arm64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh | |||||
package unix | package unix | ||||
@@ -2,7 +2,8 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||||
// Unix environment variables. | // Unix environment variables. | ||||
@@ -0,0 +1,221 @@ | |||||
// Copyright 2020 The Go Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
//go:build zos && s390x | |||||
// +build zos,s390x | |||||
package unix | |||||
import ( | |||||
"sync" | |||||
) | |||||
// This file simulates epoll on z/OS using poll. | |||||
// Analogous to epoll_event on Linux. | |||||
// TODO(neeilan): Pad is because the Linux kernel expects a 96-bit struct. We never pass this to the kernel; remove? | |||||
type EpollEvent struct { | |||||
Events uint32 | |||||
Fd int32 | |||||
Pad int32 | |||||
} | |||||
const ( | |||||
EPOLLERR = 0x8 | |||||
EPOLLHUP = 0x10 | |||||
EPOLLIN = 0x1 | |||||
EPOLLMSG = 0x400 | |||||
EPOLLOUT = 0x4 | |||||
EPOLLPRI = 0x2 | |||||
EPOLLRDBAND = 0x80 | |||||
EPOLLRDNORM = 0x40 | |||||
EPOLLWRBAND = 0x200 | |||||
EPOLLWRNORM = 0x100 | |||||
EPOLL_CTL_ADD = 0x1 | |||||
EPOLL_CTL_DEL = 0x2 | |||||
EPOLL_CTL_MOD = 0x3 | |||||
// The following constants are part of the epoll API, but represent | |||||
// currently unsupported functionality on z/OS. | |||||
// EPOLL_CLOEXEC = 0x80000 | |||||
// EPOLLET = 0x80000000 | |||||
// EPOLLONESHOT = 0x40000000 | |||||
// EPOLLRDHUP = 0x2000 // Typically used with edge-triggered notis | |||||
// EPOLLEXCLUSIVE = 0x10000000 // Exclusive wake-up mode | |||||
// EPOLLWAKEUP = 0x20000000 // Relies on Linux's BLOCK_SUSPEND capability | |||||
) | |||||
// TODO(neeilan): We can eliminate these epToPoll / pToEpoll calls by using identical mask values for POLL/EPOLL | |||||
// constants where possible The lower 16 bits of epoll events (uint32) can fit any system poll event (int16). | |||||
// epToPollEvt converts epoll event field to poll equivalent. | |||||
// In epoll, Events is a 32-bit field, while poll uses 16 bits. | |||||
func epToPollEvt(events uint32) int16 { | |||||
var ep2p = map[uint32]int16{ | |||||
EPOLLIN: POLLIN, | |||||
EPOLLOUT: POLLOUT, | |||||
EPOLLHUP: POLLHUP, | |||||
EPOLLPRI: POLLPRI, | |||||
EPOLLERR: POLLERR, | |||||
} | |||||
var pollEvts int16 = 0 | |||||
for epEvt, pEvt := range ep2p { | |||||
if (events & epEvt) != 0 { | |||||
pollEvts |= pEvt | |||||
} | |||||
} | |||||
return pollEvts | |||||
} | |||||
// pToEpollEvt converts 16 bit poll event bitfields to 32-bit epoll event fields. | |||||
func pToEpollEvt(revents int16) uint32 { | |||||
var p2ep = map[int16]uint32{ | |||||
POLLIN: EPOLLIN, | |||||
POLLOUT: EPOLLOUT, | |||||
POLLHUP: EPOLLHUP, | |||||
POLLPRI: EPOLLPRI, | |||||
POLLERR: EPOLLERR, | |||||
} | |||||
var epollEvts uint32 = 0 | |||||
for pEvt, epEvt := range p2ep { | |||||
if (revents & pEvt) != 0 { | |||||
epollEvts |= epEvt | |||||
} | |||||
} | |||||
return epollEvts | |||||
} | |||||
// Per-process epoll implementation. | |||||
type epollImpl struct { | |||||
mu sync.Mutex | |||||
epfd2ep map[int]*eventPoll | |||||
nextEpfd int | |||||
} | |||||
// eventPoll holds a set of file descriptors being watched by the process. A process can have multiple epoll instances. | |||||
// On Linux, this is an in-kernel data structure accessed through a fd. | |||||
type eventPoll struct { | |||||
mu sync.Mutex | |||||
fds map[int]*EpollEvent | |||||
} | |||||
// epoll impl for this process. | |||||
var impl epollImpl = epollImpl{ | |||||
epfd2ep: make(map[int]*eventPoll), | |||||
nextEpfd: 0, | |||||
} | |||||
func (e *epollImpl) epollcreate(size int) (epfd int, err error) { | |||||
e.mu.Lock() | |||||
defer e.mu.Unlock() | |||||
epfd = e.nextEpfd | |||||
e.nextEpfd++ | |||||
e.epfd2ep[epfd] = &eventPoll{ | |||||
fds: make(map[int]*EpollEvent), | |||||
} | |||||
return epfd, nil | |||||
} | |||||
func (e *epollImpl) epollcreate1(flag int) (fd int, err error) { | |||||
return e.epollcreate(4) | |||||
} | |||||
func (e *epollImpl) epollctl(epfd int, op int, fd int, event *EpollEvent) (err error) { | |||||
e.mu.Lock() | |||||
defer e.mu.Unlock() | |||||
ep, ok := e.epfd2ep[epfd] | |||||
if !ok { | |||||
return EBADF | |||||
} | |||||
switch op { | |||||
case EPOLL_CTL_ADD: | |||||
// TODO(neeilan): When we make epfds and fds disjoint, detect epoll | |||||
// loops here (instances watching each other) and return ELOOP. | |||||
if _, ok := ep.fds[fd]; ok { | |||||
return EEXIST | |||||
} | |||||
ep.fds[fd] = event | |||||
case EPOLL_CTL_MOD: | |||||
if _, ok := ep.fds[fd]; !ok { | |||||
return ENOENT | |||||
} | |||||
ep.fds[fd] = event | |||||
case EPOLL_CTL_DEL: | |||||
if _, ok := ep.fds[fd]; !ok { | |||||
return ENOENT | |||||
} | |||||
delete(ep.fds, fd) | |||||
} | |||||
return nil | |||||
} | |||||
// Must be called while holding ep.mu | |||||
func (ep *eventPoll) getFds() []int { | |||||
fds := make([]int, len(ep.fds)) | |||||
for fd := range ep.fds { | |||||
fds = append(fds, fd) | |||||
} | |||||
return fds | |||||
} | |||||
func (e *epollImpl) epollwait(epfd int, events []EpollEvent, msec int) (n int, err error) { | |||||
e.mu.Lock() // in [rare] case of concurrent epollcreate + epollwait | |||||
ep, ok := e.epfd2ep[epfd] | |||||
if !ok { | |||||
e.mu.Unlock() | |||||
return 0, EBADF | |||||
} | |||||
pollfds := make([]PollFd, 4) | |||||
for fd, epollevt := range ep.fds { | |||||
pollfds = append(pollfds, PollFd{Fd: int32(fd), Events: epToPollEvt(epollevt.Events)}) | |||||
} | |||||
e.mu.Unlock() | |||||
n, err = Poll(pollfds, msec) | |||||
if err != nil { | |||||
return n, err | |||||
} | |||||
i := 0 | |||||
for _, pFd := range pollfds { | |||||
if pFd.Revents != 0 { | |||||
events[i] = EpollEvent{Fd: pFd.Fd, Events: pToEpollEvt(pFd.Revents)} | |||||
i++ | |||||
} | |||||
if i == n { | |||||
break | |||||
} | |||||
} | |||||
return n, nil | |||||
} | |||||
func EpollCreate(size int) (fd int, err error) { | |||||
return impl.epollcreate(size) | |||||
} | |||||
func EpollCreate1(flag int) (fd int, err error) { | |||||
return impl.epollcreate1(flag) | |||||
} | |||||
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { | |||||
return impl.epollctl(epfd, op, fd, event) | |||||
} | |||||
// Because EpollWait mutates events, the caller is expected to coordinate | |||||
// concurrent access if calling with the same epfd from multiple goroutines. | |||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { | |||||
return impl.epollwait(epfd, events, msec) | |||||
} |
@@ -2,6 +2,7 @@ | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
//go:build dragonfly || freebsd || linux || netbsd || openbsd | |||||
// +build dragonfly freebsd linux netbsd openbsd | // +build dragonfly freebsd linux netbsd openbsd | ||||
package unix | package unix | ||||