diff --git a/src/crypto/dsa/dsa.go b/src/crypto/dsa/dsa.go
index 43826bcb55..a83359996d 100644
--- a/src/crypto/dsa/dsa.go
+++ b/src/crypto/dsa/dsa.go
@@ -5,6 +5,12 @@
// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3.
//
// The DSA operations in this package are not implemented using constant-time algorithms.
+//
+// Deprecated: DSA is a legacy algorithm, and modern alternatives such as
+// Ed25519 (implemented by package crypto/ed25519) should be used instead. Keys
+// with 1024-bit moduli (L1024N160 parameters) are cryptographically weak, while
+// bigger keys are not widely supported. Note that FIPS 186-5 no longer approves
+// DSA for signature generation.
package dsa
import (
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index 93dca03840..58c4aa360f 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -159,10 +159,6 @@ type dsaAlgorithmParameters struct {
P, Q, G *big.Int
}
-type dsaSignature struct {
- R, S *big.Int
-}
-
type validity struct {
NotBefore, NotAfter time.Time
}
@@ -182,14 +178,15 @@ type SignatureAlgorithm int
const (
UnknownSignatureAlgorithm SignatureAlgorithm = iota
- MD2WithRSA
- MD5WithRSA
+
+ MD2WithRSA // Unsupported.
+ MD5WithRSA // Only supported for signing, not verification.
SHA1WithRSA
SHA256WithRSA
SHA384WithRSA
SHA512WithRSA
- DSAWithSHA1
- DSAWithSHA256
+ DSAWithSHA1 // Unsupported.
+ DSAWithSHA256 // Unsupported.
ECDSAWithSHA1
ECDSAWithSHA256
ECDSAWithSHA384
@@ -223,7 +220,7 @@ type PublicKeyAlgorithm int
const (
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
RSA
- DSA
+ DSA // Unsupported.
ECDSA
Ed25519
)
@@ -845,28 +842,6 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
} else {
return rsa.VerifyPKCS1v15(pub, hashType, signed, signature)
}
- case *dsa.PublicKey:
- if pubKeyAlgo != DSA {
- return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
- }
- dsaSig := new(dsaSignature)
- if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil {
- return err
- } else if len(rest) != 0 {
- return errors.New("x509: trailing data after DSA signature")
- }
- if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
- return errors.New("x509: DSA signature contained zero or negative values")
- }
- // According to FIPS 186-3, section 4.6, the hash must be truncated if it is longer
- // than the key length, but crypto/dsa doesn't do it automatically.
- if maxHashLen := pub.Q.BitLen() / 8; maxHashLen < len(signed) {
- signed = signed[:maxHashLen]
- }
- if !dsa.Verify(pub, signed, dsaSig.R, dsaSig.S) {
- return errors.New("x509: DSA verification failure")
- }
- return
case *ecdsa.PublicKey:
if pubKeyAlgo != ECDSA {
return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index e87294bde5..2d9ace4a16 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -988,51 +988,8 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) {
t.Fatalf("Failed to parse certificate: %s", err)
}
// test cert is self-signed
- if err = cert.CheckSignatureFrom(cert); err != nil {
- t.Fatalf("DSA Certificate verification failed: %s", err)
- }
-}
-
-const dsaCert1024WithSha256 = `-----BEGIN CERTIFICATE-----
-MIIDKzCCAumgAwIBAgIUOXWPK4gTRZVVY7OSXTU00QEWQU8wCwYJYIZIAWUDBAMC
-MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ
-bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMTkxMDAxMDYxODUyWhgPMzAxOTAy
-MDEwNjE4NTJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
-HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggG4MIIBLAYHKoZIzjgE
-ATCCAR8CgYEAr79m/1ypU1aUbbLX1jikTyX7w2QYP+EkxNtXUiiTuxkC1KBqqxT3
-0Aht2vxFR47ODEK4B79rHO+UevhaqDaAHSH7Z/9umS0h0aS32KLDLb+LI5AneCrn
-eW5YbVhfD03N7uR4kKUCKOnWj5hAk9xiE3y7oFR0bBXzqrrHJF9LMd0CFQCB6lSj
-HSW0rGmNxIZsBl72u7JFLQKBgQCOFd1PGEQmddn0cdFgby5QQfjrqmoD1zNlFZEt
-L0x1EbndFwelLlF1ChNh3NPNUkjwRbla07FDlONs1GMJq6w4vW11ns+pUvAZ2+RM
-EVFjugip8az2ncn3UujGTVdFxnSTLBsRlMP/tFDK3ky//8zn/5ha9SKKw4v1uv6M
-JuoIbwOBhQACgYEAoeKeR90nwrnoPi5MOUPBLQvuzB87slfr+3kL8vFCmgjA6MtB
-7TxQKoBTOo5aVgWDp0lMIMxLd6btzBrm6r3VdRlh/cL8/PtbxkFwBa+Upe4o5NAh
-ISCe2/f2leT1PxtF8xxYjz/fszeUeHsJbVMilE2cuB2SYrR5tMExiqy+QpqjUzBR
-MB0GA1UdDgQWBBQDMIEL8Z3jc1d9wCxWtksUWc8RkjAfBgNVHSMEGDAWgBQDMIEL
-8Z3jc1d9wCxWtksUWc8RkjAPBgNVHRMBAf8EBTADAQH/MAsGCWCGSAFlAwQDAgMv
-ADAsAhQFehZgI4OyKBGpfnXvyJ0Z/0a6nAIUTO265Ane87LfJuQr3FrqvuCI354=
------END CERTIFICATE-----
-`
-
-func TestVerifyCertificateWithDSATooLongHash(t *testing.T) {
- pemBlock, _ := pem.Decode([]byte(dsaCert1024WithSha256))
- cert, err := ParseCertificate(pemBlock.Bytes)
- if err != nil {
- t.Fatalf("Failed to parse certificate: %s", err)
- }
-
- // test cert is self-signed
- if err = cert.CheckSignatureFrom(cert); err != nil {
- t.Fatalf("DSA Certificate self-signature verification failed: %s", err)
- }
-
- signed := []byte("A wild Gopher appears!\n")
- signature, _ := hex.DecodeString("302c0214417aca7ff458f5b566e43e7b82f994953da84be50214625901e249e33f4e4838f8b5966020c286dd610e")
-
- // This signature is using SHA256, but only has 1024 DSA key. The hash has to be truncated
- // in CheckSignature, otherwise it won't pass.
- if err = cert.CheckSignature(DSAWithSHA256, signed, signature); err != nil {
- t.Fatalf("DSA signature verification failed: %s", err)
+ if err = cert.CheckSignatureFrom(cert); err == nil {
+ t.Fatalf("Expected error verifying DSA certificate")
}
}
--
cgit v1.2.3-54-g00ecf
From 44a15a7262b14d517fefab5b7c13ca97ab099a30 Mon Sep 17 00:00:00 2001
From: Dai Jie
Date: Fri, 2 Oct 2020 09:09:24 +0000
Subject: net/http: remove duplicate declaration of error
there is no need to declare a error variable here.
Change-Id: I9ea5bcf568d800efed19c90caf751aaf9abe5555
GitHub-Last-Rev: 538d1f9cee0b8564a8bec262529f567da847f1b0
GitHub-Pull-Request: golang/go#41751
Reviewed-on: https://go-review.googlesource.com/c/go/+/259037
Reviewed-by: Rob Pike
Trust: Alberto Donizetti
---
src/net/rpc/client.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/net/rpc/client.go b/src/net/rpc/client.go
index 25f2a004e4..60bb2cc99f 100644
--- a/src/net/rpc/client.go
+++ b/src/net/rpc/client.go
@@ -245,7 +245,6 @@ func DialHTTP(network, address string) (*Client, error) {
// DialHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialHTTPPath(network, address, path string) (*Client, error) {
- var err error
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
--
cgit v1.2.3-54-g00ecf
From 79dbdf2a4c2ad93d3de493956f8bbca1465ba932 Mon Sep 17 00:00:00 2001
From: Rob Findley
Date: Mon, 21 Sep 2020 08:47:34 -0400
Subject: go/types: add Checker.walkDecl to simplify checking declarations
Handling ast.GenDecls while typechecking is repetitive, and a source of
diffs compared to typechecking using the cmd/compile/internal/syntax
package, which unpacks declaration groups into individual declarations.
Refactor to extract the logic for walking declarations. This introduces
a new AST abstraction: types.decl, which comes at some minor performance
cost. However, if we are to fully abstract the AST we will be paying
this cost anyway, and benchmarking suggests that the cost is negligible.
Change-Id: If73c30c3d08053ccf7bf21ef886f0452fdbf142e
Reviewed-on: https://go-review.googlesource.com/c/go/+/256298
Run-TryBot: Robert Findley
TryBot-Result: Go Bot
Reviewed-by: Robert Griesemer
Trust: Robert Findley
---
src/go/types/decl.go | 266 ++++++++++++++++++++++++++-------------------
src/go/types/resolver.go | 278 +++++++++++++++++++++--------------------------
2 files changed, 275 insertions(+), 269 deletions(-)
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 5c0e611c51..a022ec5678 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -381,6 +381,76 @@ func firstInSrc(path []Object) int {
return fst
}
+type (
+ decl interface {
+ node() ast.Node
+ }
+
+ importDecl struct{ spec *ast.ImportSpec }
+ constDecl struct {
+ spec *ast.ValueSpec
+ iota int
+ typ ast.Expr
+ init []ast.Expr
+ }
+ varDecl struct{ spec *ast.ValueSpec }
+ typeDecl struct{ spec *ast.TypeSpec }
+ funcDecl struct{ decl *ast.FuncDecl }
+)
+
+func (d importDecl) node() ast.Node { return d.spec }
+func (d constDecl) node() ast.Node { return d.spec }
+func (d varDecl) node() ast.Node { return d.spec }
+func (d typeDecl) node() ast.Node { return d.spec }
+func (d funcDecl) node() ast.Node { return d.decl }
+
+func (check *Checker) walkDecls(decls []ast.Decl, f func(decl)) {
+ for _, d := range decls {
+ check.walkDecl(d, f)
+ }
+}
+
+func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
+ switch d := d.(type) {
+ case *ast.BadDecl:
+ // ignore
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, s := range d.Specs {
+ switch s := s.(type) {
+ case *ast.ImportSpec:
+ f(importDecl{s})
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which initialization expressions to use
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ }
+ check.arityMatch(s, last)
+ f(constDecl{spec: s, iota: iota, init: last.Values, typ: last.Type})
+ case token.VAR:
+ check.arityMatch(s, nil)
+ f(varDecl{s})
+ default:
+ check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ }
+ case *ast.TypeSpec:
+ f(typeDecl{s})
+ default:
+ check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ }
+ }
+ case *ast.FuncDecl:
+ f(funcDecl{d})
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+}
+
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
assert(obj.typ == nil)
@@ -664,133 +734,105 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
}
}
-func (check *Checker) declStmt(decl ast.Decl) {
+func (check *Checker) declStmt(d ast.Decl) {
pkg := check.pkg
- switch d := decl.(type) {
- case *ast.BadDecl:
- // ignore
+ check.walkDecl(d, func(d decl) {
+ switch d := d.(type) {
+ case constDecl:
+ top := len(check.delayed)
- case *ast.GenDecl:
- var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
- for iota, spec := range d.Specs {
- switch s := spec.(type) {
- case *ast.ValueSpec:
- switch d.Tok {
- case token.CONST:
- top := len(check.delayed)
+ // declare all constants
+ lhs := make([]*Const, len(d.spec.Names))
+ for i, name := range d.spec.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(d.iota)))
+ lhs[i] = obj
- // determine which init exprs to use
- switch {
- case s.Type != nil || len(s.Values) > 0:
- last = s
- case last == nil:
- last = new(ast.ValueSpec) // make sure last exists
- }
-
- // declare all constants
- lhs := make([]*Const, len(s.Names))
- for i, name := range s.Names {
- obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
- lhs[i] = obj
-
- var init ast.Expr
- if i < len(last.Values) {
- init = last.Values[i]
- }
+ var init ast.Expr
+ if i < len(d.init) {
+ init = d.init[i]
+ }
- check.constDecl(obj, last.Type, init)
- }
+ check.constDecl(obj, d.typ, init)
+ }
- check.arityMatch(s, last)
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
- // process function literals in init expressions before scope changes
- check.processDelayed(top)
+ // spec: "The scope of a constant or variable identifier declared
+ // inside a function begins at the end of the ConstSpec or VarSpec
+ // (ShortVarDecl for short variable declarations) and ends at the
+ // end of the innermost containing block."
+ scopePos := d.spec.End()
+ for i, name := range d.spec.Names {
+ check.declare(check.scope, name, lhs[i], scopePos)
+ }
- // spec: "The scope of a constant or variable identifier declared
- // inside a function begins at the end of the ConstSpec or VarSpec
- // (ShortVarDecl for short variable declarations) and ends at the
- // end of the innermost containing block."
- scopePos := s.End()
- for i, name := range s.Names {
- check.declare(check.scope, name, lhs[i], scopePos)
- }
+ case varDecl:
+ top := len(check.delayed)
- case token.VAR:
- top := len(check.delayed)
+ lhs0 := make([]*Var, len(d.spec.Names))
+ for i, name := range d.spec.Names {
+ lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ }
- lhs0 := make([]*Var, len(s.Names))
- for i, name := range s.Names {
- lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ // initialize all variables
+ for i, obj := range lhs0 {
+ var lhs []*Var
+ var init ast.Expr
+ switch len(d.spec.Values) {
+ case len(d.spec.Names):
+ // lhs and rhs match
+ init = d.spec.Values[i]
+ case 1:
+ // rhs is expected to be a multi-valued expression
+ lhs = lhs0
+ init = d.spec.Values[0]
+ default:
+ if i < len(d.spec.Values) {
+ init = d.spec.Values[i]
}
-
- // initialize all variables
- for i, obj := range lhs0 {
- var lhs []*Var
- var init ast.Expr
- switch len(s.Values) {
- case len(s.Names):
- // lhs and rhs match
- init = s.Values[i]
- case 1:
- // rhs is expected to be a multi-valued expression
- lhs = lhs0
- init = s.Values[0]
- default:
- if i < len(s.Values) {
- init = s.Values[i]
- }
- }
- check.varDecl(obj, lhs, s.Type, init)
- if len(s.Values) == 1 {
- // If we have a single lhs variable we are done either way.
- // If we have a single rhs expression, it must be a multi-
- // valued expression, in which case handling the first lhs
- // variable will cause all lhs variables to have a type
- // assigned, and we are done as well.
- if debug {
- for _, obj := range lhs0 {
- assert(obj.typ != nil)
- }
- }
- break
+ }
+ check.varDecl(obj, lhs, d.spec.Type, init)
+ if len(d.spec.Values) == 1 {
+ // If we have a single lhs variable we are done either way.
+ // If we have a single rhs expression, it must be a multi-
+ // valued expression, in which case handling the first lhs
+ // variable will cause all lhs variables to have a type
+ // assigned, and we are done as well.
+ if debug {
+ for _, obj := range lhs0 {
+ assert(obj.typ != nil)
}
}
-
- check.arityMatch(s, nil)
-
- // process function literals in init expressions before scope changes
- check.processDelayed(top)
-
- // declare all variables
- // (only at this point are the variable scopes (parents) set)
- scopePos := s.End() // see constant declarations
- for i, name := range s.Names {
- // see constant declarations
- check.declare(check.scope, name, lhs0[i], scopePos)
- }
-
- default:
- check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ break
}
+ }
- case *ast.TypeSpec:
- obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
- // spec: "The scope of a type identifier declared inside a function
- // begins at the identifier in the TypeSpec and ends at the end of
- // the innermost containing block."
- scopePos := s.Name.Pos()
- check.declare(check.scope, s.Name, obj, scopePos)
- // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
- obj.setColor(grey + color(check.push(obj)))
- check.typeDecl(obj, s.Type, nil, s.Assign.IsValid())
- check.pop().setColor(black)
- default:
- check.invalidAST(s.Pos(), "const, type, or var declaration expected")
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
+
+ // declare all variables
+ // (only at this point are the variable scopes (parents) set)
+ scopePos := d.spec.End() // see constant declarations
+ for i, name := range d.spec.Names {
+ // see constant declarations
+ check.declare(check.scope, name, lhs0[i], scopePos)
}
- }
- default:
- check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
- }
+ case typeDecl:
+ obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
+ // spec: "The scope of a type identifier declared inside a function
+ // begins at the identifier in the TypeSpec and ends at the end of
+ // the innermost containing block."
+ scopePos := d.spec.Name.Pos()
+ check.declare(check.scope, d.spec.Name, obj, scopePos)
+ // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
+ obj.setColor(grey + color(check.push(obj)))
+ check.typeDecl(obj, d.spec.Type, nil, d.spec.Assign.IsValid())
+ check.pop().setColor(black)
+ default:
+ check.invalidAST(d.node().Pos(), "unknown ast.Decl node %T", d.node())
+ }
+ })
}
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 078adc5ec7..cce222cbc5 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -235,179 +235,147 @@ func (check *Checker) collectObjects() {
// we get "." as the directory which is what we would want.
fileDir := dir(check.fset.Position(file.Name.Pos()).Filename)
- for _, decl := range file.Decls {
- switch d := decl.(type) {
- case *ast.BadDecl:
- // ignore
-
- case *ast.GenDecl:
- var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
- for iota, spec := range d.Specs {
- switch s := spec.(type) {
- case *ast.ImportSpec:
- // import package
- path, err := validatedImportPath(s.Path.Value)
- if err != nil {
- check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
- continue
- }
+ check.walkDecls(file.Decls, func(d decl) {
+ switch d := d.(type) {
+ case importDecl:
+ // import package
+ path, err := validatedImportPath(d.spec.Path.Value)
+ if err != nil {
+ check.errorf(d.spec.Path.Pos(), "invalid import path (%s)", err)
+ return
+ }
- imp := check.importPackage(s.Path.Pos(), path, fileDir)
- if imp == nil {
- continue
- }
+ imp := check.importPackage(d.spec.Path.Pos(), path, fileDir)
+ if imp == nil {
+ return
+ }
- // add package to list of explicit imports
- // (this functionality is provided as a convenience
- // for clients; it is not needed for type-checking)
- if !pkgImports[imp] {
- pkgImports[imp] = true
- pkg.imports = append(pkg.imports, imp)
- }
+ // add package to list of explicit imports
+ // (this functionality is provided as a convenience
+ // for clients; it is not needed for type-checking)
+ if !pkgImports[imp] {
+ pkgImports[imp] = true
+ pkg.imports = append(pkg.imports, imp)
+ }
- // local name overrides imported package name
- name := imp.name
- if s.Name != nil {
- name = s.Name.Name
- if path == "C" {
- // match cmd/compile (not prescribed by spec)
- check.errorf(s.Name.Pos(), `cannot rename import "C"`)
- continue
- }
- if name == "init" {
- check.errorf(s.Name.Pos(), "cannot declare init - must be func")
- continue
- }
- }
+ // local name overrides imported package name
+ name := imp.name
+ if d.spec.Name != nil {
+ name = d.spec.Name.Name
+ if path == "C" {
+ // match cmd/compile (not prescribed by spec)
+ check.errorf(d.spec.Name.Pos(), `cannot rename import "C"`)
+ return
+ }
+ if name == "init" {
+ check.errorf(d.spec.Name.Pos(), "cannot declare init - must be func")
+ return
+ }
+ }
- obj := NewPkgName(s.Pos(), pkg, name, imp)
- if s.Name != nil {
- // in a dot-import, the dot represents the package
- check.recordDef(s.Name, obj)
- } else {
- check.recordImplicit(s, obj)
- }
+ obj := NewPkgName(d.spec.Pos(), pkg, name, imp)
+ if d.spec.Name != nil {
+ // in a dot-import, the dot represents the package
+ check.recordDef(d.spec.Name, obj)
+ } else {
+ check.recordImplicit(d.spec, obj)
+ }
- if path == "C" {
- // match cmd/compile (not prescribed by spec)
- obj.used = true
- }
+ if path == "C" {
+ // match cmd/compile (not prescribed by spec)
+ obj.used = true
+ }
- // add import to file scope
- if name == "." {
- // merge imported scope with file scope
- for _, obj := range imp.scope.elems {
- // A package scope may contain non-exported objects,
- // do not import them!
- if obj.Exported() {
- // declare dot-imported object
- // (Do not use check.declare because it modifies the object
- // via Object.setScopePos, which leads to a race condition;
- // the object may be imported into more than one file scope
- // concurrently. See issue #32154.)
- if alt := fileScope.Insert(obj); alt != nil {
- check.errorf(s.Name.Pos(), "%s redeclared in this block", obj.Name())
- check.reportAltDecl(alt)
- }
- }
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range imp.scope.elems {
+ // A package scope may contain non-exported objects,
+ // do not import them!
+ if obj.Exported() {
+ // declare dot-imported object
+ // (Do not use check.declare because it modifies the object
+ // via Object.setScopePos, which leads to a race condition;
+ // the object may be imported into more than one file scope
+ // concurrently. See issue #32154.)
+ if alt := fileScope.Insert(obj); alt != nil {
+ check.errorf(d.spec.Name.Pos(), "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
}
- // add position to set of dot-import positions for this file
- // (this is only needed for "imported but not used" errors)
- check.addUnusedDotImport(fileScope, imp, s.Pos())
- } else {
- // declare imported package object in file scope
- // (no need to provide s.Name since we called check.recordDef earlier)
- check.declare(fileScope, nil, obj, token.NoPos)
}
+ }
+ // add position to set of dot-import positions for this file
+ // (this is only needed for "imported but not used" errors)
+ check.addUnusedDotImport(fileScope, imp, d.spec.Pos())
+ } else {
+ // declare imported package object in file scope
+ // (no need to provide s.Name since we called check.recordDef earlier)
+ check.declare(fileScope, nil, obj, token.NoPos)
+ }
+ case constDecl:
+ // declare all constants
+ for i, name := range d.spec.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(d.iota)))
+
+ var init ast.Expr
+ if i < len(d.init) {
+ init = d.init[i]
+ }
- case *ast.ValueSpec:
- switch d.Tok {
- case token.CONST:
- // determine which initialization expressions to use
- switch {
- case s.Type != nil || len(s.Values) > 0:
- last = s
- case last == nil:
- last = new(ast.ValueSpec) // make sure last exists
- }
-
- // declare all constants
- for i, name := range s.Names {
- obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
-
- var init ast.Expr
- if i < len(last.Values) {
- init = last.Values[i]
- }
-
- d := &declInfo{file: fileScope, typ: last.Type, init: init}
- check.declarePkgObj(name, obj, d)
- }
-
- check.arityMatch(s, last)
-
- case token.VAR:
- lhs := make([]*Var, len(s.Names))
- // If there's exactly one rhs initializer, use
- // the same declInfo d1 for all lhs variables
- // so that each lhs variable depends on the same
- // rhs initializer (n:1 var declaration).
- var d1 *declInfo
- if len(s.Values) == 1 {
- // The lhs elements are only set up after the for loop below,
- // but that's ok because declareVar only collects the declInfo
- // for a later phase.
- d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
- }
-
- // declare all variables
- for i, name := range s.Names {
- obj := NewVar(name.Pos(), pkg, name.Name, nil)
- lhs[i] = obj
-
- d := d1
- if d == nil {
- // individual assignments
- var init ast.Expr
- if i < len(s.Values) {
- init = s.Values[i]
- }
- d = &declInfo{file: fileScope, typ: s.Type, init: init}
- }
-
- check.declarePkgObj(name, obj, d)
- }
+ d := &declInfo{file: fileScope, typ: d.typ, init: init}
+ check.declarePkgObj(name, obj, d)
+ }
- check.arityMatch(s, nil)
+ case varDecl:
+ lhs := make([]*Var, len(d.spec.Names))
+ // If there's exactly one rhs initializer, use
+ // the same declInfo d1 for all lhs variables
+ // so that each lhs variable depends on the same
+ // rhs initializer (n:1 var declaration).
+ var d1 *declInfo
+ if len(d.spec.Values) == 1 {
+ // The lhs elements are only set up after the for loop below,
+ // but that's ok because declareVar only collects the declInfo
+ // for a later phase.
+ d1 = &declInfo{file: fileScope, lhs: lhs, typ: d.spec.Type, init: d.spec.Values[0]}
+ }
- default:
- check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ // declare all variables
+ for i, name := range d.spec.Names {
+ obj := NewVar(name.Pos(), pkg, name.Name, nil)
+ lhs[i] = obj
+
+ di := d1
+ if di == nil {
+ // individual assignments
+ var init ast.Expr
+ if i < len(d.spec.Values) {
+ init = d.spec.Values[i]
}
-
- case *ast.TypeSpec:
- obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
- check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()})
-
- default:
- check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ di = &declInfo{file: fileScope, typ: d.spec.Type, init: init}
}
- }
- case *ast.FuncDecl:
- name := d.Name.Name
- obj := NewFunc(d.Name.Pos(), pkg, name, nil)
- if d.Recv == nil {
+ check.declarePkgObj(name, obj, di)
+ }
+ case typeDecl:
+ obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
+ check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, typ: d.spec.Type, alias: d.spec.Assign.IsValid()})
+ case funcDecl:
+ info := &declInfo{file: fileScope, fdecl: d.decl}
+ name := d.decl.Name.Name
+ obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
+ if d.decl.Recv == nil {
// regular function
if name == "init" {
// don't declare init functions in the package scope - they are invisible
obj.parent = pkg.scope
- check.recordDef(d.Name, obj)
+ check.recordDef(d.decl.Name, obj)
// init functions must have a body
- if d.Body == nil {
+ if d.decl.Body == nil {
check.softErrorf(obj.pos, "missing function body")
}
} else {
- check.declare(pkg.scope, d.Name, obj, token.NoPos)
+ check.declare(pkg.scope, d.decl.Name, obj, token.NoPos)
}
} else {
// method
@@ -417,20 +385,16 @@ func (check *Checker) collectObjects() {
if name != "_" {
methods = append(methods, obj)
}
- check.recordDef(d.Name, obj)
+ check.recordDef(d.decl.Name, obj)
}
- info := &declInfo{file: fileScope, fdecl: d}
// Methods are not package-level objects but we still track them in the
// object map so that we can handle them like regular functions (if the
// receiver is invalid); also we need their fdecl info when associating
// them with their receiver base type, below.
check.objMap[obj] = info
obj.setOrder(uint32(len(check.objMap)))
-
- default:
- check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
- }
+ })
}
// verify that objects in package and file scopes have different names
--
cgit v1.2.3-54-g00ecf
From d888f1d5c06828e9d7b0166f770a443f6315c2d1 Mon Sep 17 00:00:00 2001
From: Austin Clements
Date: Thu, 1 Oct 2020 16:06:03 -0400
Subject: runtime: add debugging to TestTimePprof
We've seen timeouts of TestTimePprof, but the tracebacks aren't useful
because goroutines are running on other threads. Add GOTRACEBACK=crash
to catch these in the future.
For #41120.
Change-Id: I97318172ef78d0cbab10df5e4ffcbfeadff579e3
Reviewed-on: https://go-review.googlesource.com/c/go/+/258802
Trust: Austin Clements
Run-TryBot: Austin Clements
TryBot-Result: Go Bot
Reviewed-by: Bryan C. Mills
---
src/runtime/crash_test.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index 34f30c9a37..eae4f538c1 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -667,7 +667,9 @@ func TestBadTraceback(t *testing.T) {
}
func TestTimePprof(t *testing.T) {
- fn := runTestProg(t, "testprog", "TimeProf")
+ // Pass GOTRACEBACK for issue #41120 to try to get more
+ // information on timeout.
+ fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
fn = strings.TrimSpace(fn)
defer os.Remove(fn)
--
cgit v1.2.3-54-g00ecf
From 21eb3dcf93fc3698c9b8cd3ba83c9ddbef31e880 Mon Sep 17 00:00:00 2001
From: Tobias Klauser
Date: Fri, 2 Oct 2020 22:13:14 +0200
Subject: doc/go1.16: announce netbsd/arm64 support
netbsd/arm64 now complies with all the requirements for a port as
specified on https://golang.org/wiki/PortingPolicy
Note that this was preliminarily announced in the Go 1.13 release notes
(CL 183637) but then removed again due to the port lacking a builder at
that time (CL 192997).
Updates #30824
Change-Id: I2f40fabc84fe9cb699282e6a9d13ed9b64478e36
Reviewed-on: https://go-review.googlesource.com/c/go/+/259277
Trust: Tobias Klauser
Reviewed-by: Ian Lance Taylor
---
doc/go1.16.html | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/doc/go1.16.html b/doc/go1.16.html
index 5e0fa60e2f..f7bcb9e94f 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -31,8 +31,11 @@ Do not send CLs removing the interior tags from such phrases.
Ports
-
- TODO
+
NetBSD
+
+
+ Go now supports the 64-bit ARM architecture on NetBSD (the
+ netbsd/arm64
port).
--
cgit v1.2.3-54-g00ecf
From f89d05eb7ba1885474d03bb62f0a36a2d3cf56ea Mon Sep 17 00:00:00 2001
From: Austin Clements
Date: Thu, 1 Oct 2020 10:58:47 -0400
Subject: runtime: update and tidy cgo callback description
The documentation on how cgo callbacks (C -> Go calls) works
internally has gotten somewhat stale. This CL refreshes it.
Change-Id: I1ab66225c9da52d698d97ebeb4f3c7b9b5ee97db
Reviewed-on: https://go-review.googlesource.com/c/go/+/258937
Trust: Austin Clements
Reviewed-by: Ian Lance Taylor
Reviewed-by: Cherry Zhang
---
src/runtime/cgocall.go | 42 +++++++++++++++++++++++-------------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index 427ed0ffb9..0b69ff3233 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -35,44 +35,48 @@
// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't
// know about packages). The gcc-compiled C function f calls GoF.
//
-// GoF calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2
-// (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument
-// adapter from the gcc function call ABI to the 6c function call ABI.
-// It is called from gcc to call 6c functions. In this case it calls
-// _cgoexp_GoF(frame, framesize), still running on m->g0's stack
+// GoF calls crosscall2(_cgoexp_GoF, frame, framesize, ctxt).
+// Crosscall2 (in cgo/asm_$GOARCH.s) is a four-argument adapter from
+// the gcc function call ABI to the gc function call ABI.
+// It is called from gcc to call gc functions. In this case it calls
+// _cgoexp_GoF(frame, framesize), still running on m.g0's stack
// and outside the $GOMAXPROCS limit. Thus, this code cannot yet
// call arbitrary Go code directly and must be careful not to allocate
-// memory or use up m->g0's stack.
+// memory or use up m.g0's stack.
//
-// _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize, ctxt).
+// _cgoexp_GoF (generated by cmd/cgo) calls
+// runtime.cgocallback(funcPC(p.GoF), frame, framesize, ctxt).
// (The reason for having _cgoexp_GoF instead of writing a crosscall3
// to make this call directly is that _cgoexp_GoF, because it is compiled
-// with 6c instead of gcc, can refer to dotted names like
+// with gc instead of gcc, can refer to dotted names like
// runtime.cgocallback and p.GoF.)
//
-// runtime.cgocallback (in asm_$GOARCH.s) switches from m->g0's
-// stack to the original g (m->curg)'s stack, on which it calls
+// runtime.cgocallback (in asm_$GOARCH.s) turns the raw PC of p.GoF
+// into a Go function value and calls runtime.cgocallback_gofunc.
+//
+// runtime.cgocallback_gofunc (in asm_$GOARCH.s) switches from m.g0's
+// stack to the original g (m.curg)'s stack, on which it calls
// runtime.cgocallbackg(p.GoF, frame, framesize).
// As part of the stack switch, runtime.cgocallback saves the current
-// SP as m->g0->sched.sp, so that any use of m->g0's stack during the
+// SP as m.g0.sched.sp, so that any use of m.g0's stack during the
// execution of the callback will be done below the existing stack frames.
-// Before overwriting m->g0->sched.sp, it pushes the old value on the
-// m->g0 stack, so that it can be restored later.
+// Before overwriting m.g0.sched.sp, it pushes the old value on the
+// m.g0 stack, so that it can be restored later.
//
// runtime.cgocallbackg (below) is now running on a real goroutine
-// stack (not an m->g0 stack). First it calls runtime.exitsyscall, which will
+// stack (not an m.g0 stack). First it calls runtime.exitsyscall, which will
// block until the $GOMAXPROCS limit allows running this goroutine.
// Once exitsyscall has returned, it is safe to do things like call the memory
// allocator or invoke the Go callback function p.GoF. runtime.cgocallbackg
-// first defers a function to unwind m->g0.sched.sp, so that if p.GoF
-// panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack
-// and the m->curg stack will be unwound in lock step.
+// first defers a function to unwind m.g0.sched.sp, so that if p.GoF
+// panics, m.g0.sched.sp will be restored to its old value: the m.g0 stack
+// and the m.curg stack will be unwound in lock step.
// Then it calls p.GoF. Finally it pops but does not execute the deferred
// function, calls runtime.entersyscall, and returns to runtime.cgocallback.
//
// After it regains control, runtime.cgocallback switches back to
-// m->g0's stack (the pointer is still in m->g0.sched.sp), restores the old
-// m->g0.sched.sp value from the stack, and returns to _cgoexp_GoF.
+// m.g0's stack (the pointer is still in m.g0.sched.sp), restores the old
+// m.g0.sched.sp value from the stack, and returns to _cgoexp_GoF.
//
// _cgoexp_GoF immediately returns to crosscall2, which restores the
// callee-save registers for gcc and returns to GoF, which returns to f.
--
cgit v1.2.3-54-g00ecf
From 095e0f48a19fa3bd7901f79420374b9cb50940e9 Mon Sep 17 00:00:00 2001
From: Alberto Donizetti
Date: Thu, 1 Oct 2020 12:03:27 +0200
Subject: cmd/compile: change mustHeapAlloc to return a reason why
This change renames mustHeapAlloc to heapAllocReason, and changes it
to return the reason why the argument must escape, so we don't have to
re-deduce it in its callers just to print the escape reason. It also
embeds isSmallMakeSlice body in heapAllocReason, since the former was
only used by the latter, and deletes isSmallMakeSlice.
An outdated TODO to remove smallintconst, which the TODO claimed was
only used in one place, was also removed, since grepping shows we
currently call smallintconst in 11 different places.
Change-Id: I0bd11bf29b92c4126f5bb455877ff73217d5a155
Reviewed-on: https://go-review.googlesource.com/c/go/+/258678
Run-TryBot: Alberto Donizetti
TryBot-Result: Go Bot
Trust: Alberto Donizetti
Trust: Cuong Manh Le
Reviewed-by: Cuong Manh Le
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/const.go | 1 -
src/cmd/compile/internal/gc/esc.go | 31 +++++++++++++++++++++----------
src/cmd/compile/internal/gc/escape.go | 6 +-----
src/cmd/compile/internal/gc/walk.go | 17 ++---------------
test/fixedbugs/issue41635.go | 11 +++++------
5 files changed, 29 insertions(+), 37 deletions(-)
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index c0ed8192d9..d881be485e 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -1134,7 +1134,6 @@ func strlit(n *Node) string {
return n.Val().U.(string)
}
-// TODO(gri) smallintconst is only used in one place - can we used indexconst?
func smallintconst(n *Node) bool {
if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil {
switch simtype[n.Type.Etype] {
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 375331d1f5..d7aa72b450 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -169,36 +169,47 @@ func mayAffectMemory(n *Node) bool {
}
}
-func mustHeapAlloc(n *Node) bool {
+// heapAllocReason returns the reason the given Node must be heap
+// allocated, or the empty string if it doesn't.
+func heapAllocReason(n *Node) string {
if n.Type == nil {
- return false
+ return ""
}
// Parameters are always passed via the stack.
if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) {
- return false
+ return ""
}
if n.Type.Width > maxStackVarSize {
- return true
+ return "too large for stack"
}
if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize {
- return true
+ return "too large for stack"
}
if n.Op == OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
- return true
+ return "too large for stack"
}
if n.Op == OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize {
- return true
+ return "too large for stack"
}
- if n.Op == OMAKESLICE && !isSmallMakeSlice(n) {
- return true
+ if n.Op == OMAKESLICE {
+ r := n.Right
+ if r == nil {
+ r = n.Left
+ }
+ if !smallintconst(r) {
+ return "non-constant size"
+ }
+ if t := n.Type; t.Elem().Width != 0 && r.Int64() >= maxImplicitStackVarSize/t.Elem().Width {
+ return "too large for stack"
+ }
}
- return false
+ return ""
}
// addrescapes tags node n as having had its address taken
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
index d79d32ec48..79df584ab1 100644
--- a/src/cmd/compile/internal/gc/escape.go
+++ b/src/cmd/compile/internal/gc/escape.go
@@ -1051,11 +1051,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
}
n.SetOpt(loc)
- if mustHeapAlloc(n) {
- why := "too large for stack"
- if n.Op == OMAKESLICE && (!Isconst(n.Left, CTINT) || (n.Right != nil && !Isconst(n.Right, CTINT))) {
- why = "non-constant size"
- }
+ if why := heapAllocReason(n); why != "" {
e.flow(e.heapHole().addr(n, why), loc)
}
}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 8e45059eab..3fe7c3e089 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -336,19 +336,6 @@ func walkstmt(n *Node) *Node {
return n
}
-func isSmallMakeSlice(n *Node) bool {
- if n.Op != OMAKESLICE {
- return false
- }
- r := n.Right
- if r == nil {
- r = n.Left
- }
- t := n.Type
-
- return smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < maxImplicitStackVarSize/t.Elem().Width)
-}
-
// walk the whole tree of the body of an
// expression or simple statement.
// the types expressions are calculated.
@@ -1339,8 +1326,8 @@ opswitch:
yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
}
if n.Esc == EscNone {
- if !isSmallMakeSlice(n) {
- Fatalf("non-small OMAKESLICE with EscNone: %v", n)
+ if why := heapAllocReason(n); why != "" {
+ Fatalf("%v has EscNone, but %v", n, why)
}
// var arr [r]T
// n = arr[:l]
diff --git a/test/fixedbugs/issue41635.go b/test/fixedbugs/issue41635.go
index b33c1a07e7..35c0034cdd 100644
--- a/test/fixedbugs/issue41635.go
+++ b/test/fixedbugs/issue41635.go
@@ -7,12 +7,11 @@
package p
func f() { // ERROR ""
- b1 := make([]byte, 1<<17) // ERROR "too large for stack" ""
- b2 := make([]byte, 100, 1<<17) // ERROR "too large for stack" ""
-
n, m := 100, 200
- b1 = make([]byte, n) // ERROR "non-constant size" ""
- b2 = make([]byte, 100, m) // ERROR "non-constant size" ""
+ _ = make([]byte, 1<<17) // ERROR "too large for stack" ""
+ _ = make([]byte, 100, 1<<17) // ERROR "too large for stack" ""
+ _ = make([]byte, n, 1<<17) // ERROR "too large for stack" ""
- _, _ = b1, b2
+ _ = make([]byte, n) // ERROR "non-constant size" ""
+ _ = make([]byte, 100, m) // ERROR "non-constant size" ""
}
--
cgit v1.2.3-54-g00ecf
From bb48f9925cf541e7b5f4bfafb9d008671c4ace47 Mon Sep 17 00:00:00 2001
From: Joel Sing
Date: Tue, 25 Aug 2020 20:19:55 +1000
Subject: cmd/link: add support for openbsd/mips64
Update #40995
Change-Id: I2cf9b85a960f479eaa59bf58081d03a0467bc2b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/250582
Trust: Joel Sing
Run-TryBot: Joel Sing
Reviewed-by: Matthew Dempsky
TryBot-Result: Go Bot
---
src/cmd/link/internal/mips64/obj.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go
index d2dc20f5c1..01d89a209c 100644
--- a/src/cmd/link/internal/mips64/obj.go
+++ b/src/cmd/link/internal/mips64/obj.go
@@ -60,7 +60,7 @@ func Init() (*sys.Arch, ld.Arch) {
Linuxdynld: "/lib64/ld64.so.1",
Freebsddynld: "XXX",
- Openbsddynld: "XXX",
+ Openbsddynld: "/usr/libexec/ld.so",
Netbsddynld: "XXX",
Dragonflydynld: "XXX",
Solarisdynld: "XXX",
@@ -84,7 +84,8 @@ func archinit(ctxt *ld.Link) {
*ld.FlagRound = 16 * 1024
}
- case objabi.Hlinux: /* mips64 elf */
+ case objabi.Hlinux, /* mips64 elf */
+ objabi.Hopenbsd:
ld.Elfinit(ctxt)
ld.HEADR = ld.ELFRESERVE
if *ld.FlagTextAddr == -1 {
--
cgit v1.2.3-54-g00ecf
From 869c02ce1f635960bfc2f06bb52e2b4e17eaa199 Mon Sep 17 00:00:00 2001
From: Elias Naur
Date: Wed, 16 Sep 2020 15:23:58 +0200
Subject: misc/ios: add support for running programs on the iOS simulator
Update the README to mention the emulator. Remove reference to gomobile
while here; there are multiple ways to develop for iOS today, including
using the c-archive buildmode directly.
Updates #38485
Change-Id: Iccef75e646ea8e1b9bc3fc37419cc2d6bf3dfdf4
Reviewed-on: https://go-review.googlesource.com/c/go/+/255257
Run-TryBot: Elias Naur
TryBot-Result: Go Bot
Trust: Elias Naur
Reviewed-by: Cherry Zhang
---
misc/ios/README | 31 +-
misc/ios/clangwrap.sh | 20 +-
misc/ios/detect.go | 2 +-
misc/ios/go_darwin_arm_exec.go | 823 ---------------------------------
misc/ios/go_ios_exec.go | 912 +++++++++++++++++++++++++++++++++++++
src/cmd/dist/build.go | 2 +-
src/iostest.bash | 2 +-
src/runtime/cgo/gcc_darwin_arm64.c | 2 +-
8 files changed, 942 insertions(+), 852 deletions(-)
delete mode 100644 misc/ios/go_darwin_arm_exec.go
create mode 100644 misc/ios/go_ios_exec.go
diff --git a/misc/ios/README b/misc/ios/README
index d7df191414..433bcdfd8f 100644
--- a/misc/ios/README
+++ b/misc/ios/README
@@ -1,13 +1,20 @@
Go on iOS
=========
-For details on developing Go for iOS on macOS, see the documentation in the mobile
-subrepository:
+To run the standard library tests, run all.bash as usual, but with the compiler
+set to the clang wrapper that invokes clang for iOS. For example, this command runs
+ all.bash on the iOS emulator:
- https://github.com/golang/mobile
+ GOOS=ios GOARCH=amd64 CGO_ENABLED=1 CC_FOR_TARGET=$(pwd)/../misc/ios/clangwrap.sh ./all.bash
-It is necessary to set up the environment before running tests or programs directly on a
-device.
+To use the go tool to run individual programs and tests, put $GOROOT/bin into PATH to ensure
+the go_ios_$GOARCH_exec wrapper is found. For example, to run the archive/tar tests:
+
+ export PATH=$GOROOT/bin:$PATH
+ GOOS=ios GOARCH=amd64 CGO_ENABLED=1 go test archive/tar
+
+The go_ios_exec wrapper uses GOARCH to select the emulator (amd64) or the device (arm64).
+However, further setup is required to run tests or programs directly on a device.
First make sure you have a valid developer certificate and have setup your device properly
to run apps signed by your developer certificate. Then install the libimobiledevice and
@@ -29,18 +36,10 @@ which will output something similar to
export GOIOS_TEAM_ID=ZZZZZZZZ
If you have multiple devices connected, specify the device UDID with the GOIOS_DEVICE_ID
-variable. Use `idevice_id -l` to list all available UDIDs.
-
-Finally, to run the standard library tests, run all.bash as usual, but with the compiler
-set to the clang wrapper that invokes clang for iOS. For example,
+variable. Use `idevice_id -l` to list all available UDIDs. Then, setting GOARCH to arm64
+will select the device:
- GOARCH=arm64 CGO_ENABLED=1 CC_FOR_TARGET=$(pwd)/../misc/ios/clangwrap.sh ./all.bash
-
-To use the go tool directly to run programs and tests, put $GOROOT/bin into PATH to ensure
-the go_darwin_$GOARCH_exec wrapper is found. For example, to run the archive/tar tests
-
- export PATH=$GOROOT/bin:$PATH
- GOARCH=arm64 CGO_ENABLED=1 go test archive/tar
+ GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC_FOR_TARGET=$(pwd)/../misc/ios/clangwrap.sh ./all.bash
Note that the go_darwin_$GOARCH_exec wrapper uninstalls any existing app identified by
the bundle id before installing a new app. If the uninstalled app is the last app by
diff --git a/misc/ios/clangwrap.sh b/misc/ios/clangwrap.sh
index 1d6dee28a8..dca3fcc904 100755
--- a/misc/ios/clangwrap.sh
+++ b/misc/ios/clangwrap.sh
@@ -2,17 +2,19 @@
# This uses the latest available iOS SDK, which is recommended.
# To select a specific SDK, run 'xcodebuild -showsdks'
# to see the available SDKs and replace iphoneos with one of them.
-SDK=iphoneos
-SDK_PATH=`xcrun --sdk $SDK --show-sdk-path`
-export IPHONEOS_DEPLOYMENT_TARGET=5.1
-# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang.
-CLANG=`xcrun --sdk $SDK --find clang`
-
if [ "$GOARCH" == "arm64" ]; then
+ SDK=iphoneos
+ PLATFORM=ios
CLANGARCH="arm64"
else
- echo "unknown GOARCH=$GOARCH" >&2
- exit 1
+ SDK=iphonesimulator
+ PLATFORM=ios-simulator
+ CLANGARCH="x86_64"
fi
-exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -mios-version-min=10.0 "$@"
+SDK_PATH=`xcrun --sdk $SDK --show-sdk-path`
+export IPHONEOS_DEPLOYMENT_TARGET=5.1
+# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang.
+CLANG=`xcrun --sdk $SDK --find clang`
+
+exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=10.0 "$@"
diff --git a/misc/ios/detect.go b/misc/ios/detect.go
index 1d47e47c86..b4651dfbb8 100644
--- a/misc/ios/detect.go
+++ b/misc/ios/detect.go
@@ -6,7 +6,7 @@
// detect attempts to autodetect the correct
// values of the environment variables
-// used by go_darwin_arm_exec.
+// used by go_io_exec.
// detect shells out to ideviceinfo, a third party program that can
// be obtained by following the instructions at
// https://github.com/libimobiledevice/libimobiledevice.
diff --git a/misc/ios/go_darwin_arm_exec.go b/misc/ios/go_darwin_arm_exec.go
deleted file mode 100644
index cdf4b07d0a..0000000000
--- a/misc/ios/go_darwin_arm_exec.go
+++ /dev/null
@@ -1,823 +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.
-
-// This program can be used as go_darwin_arm_exec by the Go tool.
-// It executes binaries on an iOS device using the XCode toolchain
-// and the ios-deploy program: https://github.com/phonegap/ios-deploy
-//
-// This script supports an extra flag, -lldb, that pauses execution
-// just before the main program begins and allows the user to control
-// the remote lldb session. This flag is appended to the end of the
-// script's arguments and is not passed through to the underlying
-// binary.
-//
-// This script requires that three environment variables be set:
-// GOIOS_DEV_ID: The codesigning developer id or certificate identifier
-// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids.
-// GOIOS_TEAM_ID: The team id that owns the app id prefix.
-// $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these.
-package main
-
-import (
- "bytes"
- "encoding/xml"
- "errors"
- "fmt"
- "go/build"
- "io"
- "io/ioutil"
- "log"
- "net"
- "os"
- "os/exec"
- "os/signal"
- "path/filepath"
- "runtime"
- "strings"
- "syscall"
- "time"
-)
-
-const debug = false
-
-var tmpdir string
-
-var (
- devID string
- appID string
- teamID string
- bundleID string
- deviceID string
-)
-
-// lock is a file lock to serialize iOS runs. It is global to avoid the
-// garbage collector finalizing it, closing the file and releasing the
-// lock prematurely.
-var lock *os.File
-
-func main() {
- log.SetFlags(0)
- log.SetPrefix("go_darwin_arm_exec: ")
- if debug {
- log.Println(strings.Join(os.Args, " "))
- }
- if len(os.Args) < 2 {
- log.Fatal("usage: go_darwin_arm_exec a.out")
- }
-
- // e.g. B393DDEB490947F5A463FD074299B6C0AXXXXXXX
- devID = getenv("GOIOS_DEV_ID")
-
- // e.g. Z8B3JBXXXX.org.golang.sample, Z8B3JBXXXX prefix is available at
- // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID.
- appID = getenv("GOIOS_APP_ID")
-
- // e.g. Z8B3JBXXXX, available at
- // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID.
- teamID = getenv("GOIOS_TEAM_ID")
-
- // Device IDs as listed with ios-deploy -c.
- deviceID = os.Getenv("GOIOS_DEVICE_ID")
-
- parts := strings.SplitN(appID, ".", 2)
- // For compatibility with the old builders, use a fallback bundle ID
- bundleID = "golang.gotest"
- if len(parts) == 2 {
- bundleID = parts[1]
- }
-
- exitCode, err := runMain()
- if err != nil {
- log.Fatalf("%v\n", err)
- }
- os.Exit(exitCode)
-}
-
-func runMain() (int, error) {
- var err error
- tmpdir, err = ioutil.TempDir("", "go_darwin_arm_exec_")
- if err != nil {
- return 1, err
- }
- if !debug {
- defer os.RemoveAll(tmpdir)
- }
-
- appdir := filepath.Join(tmpdir, "gotest.app")
- os.RemoveAll(appdir)
-
- if err := assembleApp(appdir, os.Args[1]); err != nil {
- return 1, err
- }
-
- // This wrapper uses complicated machinery to run iOS binaries. It
- // works, but only when running one binary at a time.
- // Use a file lock to make sure only one wrapper is running at a time.
- //
- // The lock file is never deleted, to avoid concurrent locks on distinct
- // files with the same path.
- lockName := filepath.Join(os.TempDir(), "go_darwin_arm_exec-"+deviceID+".lock")
- lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666)
- if err != nil {
- return 1, err
- }
- if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
- return 1, err
- }
-
- if err := uninstall(bundleID); err != nil {
- return 1, err
- }
-
- if err := install(appdir); err != nil {
- return 1, err
- }
-
- if err := mountDevImage(); err != nil {
- return 1, err
- }
-
- // Kill any hanging debug bridges that might take up port 3222.
- exec.Command("killall", "idevicedebugserverproxy").Run()
-
- closer, err := startDebugBridge()
- if err != nil {
- return 1, err
- }
- defer closer()
-
- if err := run(appdir, bundleID, os.Args[2:]); err != nil {
- // If the lldb driver completed with an exit code, use that.
- if err, ok := err.(*exec.ExitError); ok {
- if ws, ok := err.Sys().(interface{ ExitStatus() int }); ok {
- return ws.ExitStatus(), nil
- }
- }
- return 1, err
- }
- return 0, nil
-}
-
-func getenv(envvar string) string {
- s := os.Getenv(envvar)
- if s == "" {
- log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar)
- }
- return s
-}
-
-func assembleApp(appdir, bin string) error {
- if err := os.MkdirAll(appdir, 0755); err != nil {
- return err
- }
-
- if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil {
- return err
- }
-
- pkgpath, err := copyLocalData(appdir)
- if err != nil {
- return err
- }
-
- entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
- if err := ioutil.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil {
- return err
- }
- if err := ioutil.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil {
- return err
- }
- if err := ioutil.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil {
- return err
- }
-
- cmd := exec.Command(
- "codesign",
- "-f",
- "-s", devID,
- "--entitlements", entitlementsPath,
- appdir,
- )
- if debug {
- log.Println(strings.Join(cmd.Args, " "))
- }
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Run(); err != nil {
- return fmt.Errorf("codesign: %v", err)
- }
- return nil
-}
-
-// mountDevImage ensures a developer image is mounted on the device.
-// The image contains the device lldb server for idevicedebugserverproxy
-// to connect to.
-func mountDevImage() error {
- // Check for existing mount.
- cmd := idevCmd(exec.Command("ideviceimagemounter", "-l", "-x"))
- out, err := cmd.CombinedOutput()
- if err != nil {
- os.Stderr.Write(out)
- return fmt.Errorf("ideviceimagemounter: %v", err)
- }
- var info struct {
- Dict struct {
- Data []byte `xml:",innerxml"`
- } `xml:"dict"`
- }
- if err := xml.Unmarshal(out, &info); err != nil {
- return fmt.Errorf("mountDevImage: failed to decode mount information: %v", err)
- }
- dict, err := parsePlistDict(info.Dict.Data)
- if err != nil {
- return fmt.Errorf("mountDevImage: failed to parse mount information: %v", err)
- }
- if dict["ImagePresent"] == "true" && dict["Status"] == "Complete" {
- return nil
- }
- // Some devices only give us an ImageSignature key.
- if _, exists := dict["ImageSignature"]; exists {
- return nil
- }
- // No image is mounted. Find a suitable image.
- imgPath, err := findDevImage()
- if err != nil {
- return err
- }
- sigPath := imgPath + ".signature"
- cmd = idevCmd(exec.Command("ideviceimagemounter", imgPath, sigPath))
- if out, err := cmd.CombinedOutput(); err != nil {
- os.Stderr.Write(out)
- return fmt.Errorf("ideviceimagemounter: %v", err)
- }
- return nil
-}
-
-// findDevImage use the device iOS version and build to locate a suitable
-// developer image.
-func findDevImage() (string, error) {
- cmd := idevCmd(exec.Command("ideviceinfo"))
- out, err := cmd.Output()
- if err != nil {
- return "", fmt.Errorf("ideviceinfo: %v", err)
- }
- var iosVer, buildVer string
- lines := bytes.Split(out, []byte("\n"))
- for _, line := range lines {
- spl := bytes.SplitN(line, []byte(": "), 2)
- if len(spl) != 2 {
- continue
- }
- key, val := string(spl[0]), string(spl[1])
- switch key {
- case "ProductVersion":
- iosVer = val
- case "BuildVersion":
- buildVer = val
- }
- }
- if iosVer == "" || buildVer == "" {
- return "", errors.New("failed to parse ideviceinfo output")
- }
- verSplit := strings.Split(iosVer, ".")
- if len(verSplit) > 2 {
- // Developer images are specific to major.minor ios version.
- // Cut off the patch version.
- iosVer = strings.Join(verSplit[:2], ".")
- }
- sdkBase := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"
- patterns := []string{fmt.Sprintf("%s (%s)", iosVer, buildVer), fmt.Sprintf("%s (*)", iosVer), fmt.Sprintf("%s*", iosVer)}
- for _, pattern := range patterns {
- matches, err := filepath.Glob(filepath.Join(sdkBase, pattern, "DeveloperDiskImage.dmg"))
- if err != nil {
- return "", fmt.Errorf("findDevImage: %v", err)
- }
- if len(matches) > 0 {
- return matches[0], nil
- }
- }
- return "", fmt.Errorf("failed to find matching developer image for iOS version %s build %s", iosVer, buildVer)
-}
-
-// startDebugBridge ensures that the idevicedebugserverproxy runs on
-// port 3222.
-func startDebugBridge() (func(), error) {
- errChan := make(chan error, 1)
- cmd := idevCmd(exec.Command("idevicedebugserverproxy", "3222"))
- var stderr bytes.Buffer
- cmd.Stderr = &stderr
- if err := cmd.Start(); err != nil {
- return nil, fmt.Errorf("idevicedebugserverproxy: %v", err)
- }
- go func() {
- if err := cmd.Wait(); err != nil {
- if _, ok := err.(*exec.ExitError); ok {
- errChan <- fmt.Errorf("idevicedebugserverproxy: %s", stderr.Bytes())
- } else {
- errChan <- fmt.Errorf("idevicedebugserverproxy: %v", err)
- }
- }
- errChan <- nil
- }()
- closer := func() {
- cmd.Process.Kill()
- <-errChan
- }
- // Dial localhost:3222 to ensure the proxy is ready.
- delay := time.Second / 4
- for attempt := 0; attempt < 5; attempt++ {
- conn, err := net.DialTimeout("tcp", "localhost:3222", 5*time.Second)
- if err == nil {
- conn.Close()
- return closer, nil
- }
- select {
- case <-time.After(delay):
- delay *= 2
- case err := <-errChan:
- return nil, err
- }
- }
- closer()
- return nil, errors.New("failed to set up idevicedebugserverproxy")
-}
-
-// findDeviceAppPath returns the device path to the app with the
-// given bundle ID. It parses the output of ideviceinstaller -l -o xml,
-// looking for the bundle ID and the corresponding Path value.
-func findDeviceAppPath(bundleID string) (string, error) {
- cmd := idevCmd(exec.Command("ideviceinstaller", "-l", "-o", "xml"))
- out, err := cmd.CombinedOutput()
- if err != nil {
- os.Stderr.Write(out)
- return "", fmt.Errorf("ideviceinstaller: -l -o xml %v", err)
- }
- var list struct {
- Apps []struct {
- Data []byte `xml:",innerxml"`
- } `xml:"array>dict"`
- }
- if err := xml.Unmarshal(out, &list); err != nil {
- return "", fmt.Errorf("failed to parse ideviceinstaller output: %v", err)
- }
- for _, app := range list.Apps {
- values, err := parsePlistDict(app.Data)
- if err != nil {
- return "", fmt.Errorf("findDeviceAppPath: failed to parse app dict: %v", err)
- }
- if values["CFBundleIdentifier"] == bundleID {
- if path, ok := values["Path"]; ok {
- return path, nil
- }
- }
- }
- return "", fmt.Errorf("failed to find device path for bundle: %s", bundleID)
-}
-
-// Parse an xml encoded plist. Plist values are mapped to string.
-func parsePlistDict(dict []byte) (map[string]string, error) {
- d := xml.NewDecoder(bytes.NewReader(dict))
- values := make(map[string]string)
- var key string
- var hasKey bool
- for {
- tok, err := d.Token()
- if err == io.EOF {
- break
- }
- if err != nil {
- return nil, err
- }
- if tok, ok := tok.(xml.StartElement); ok {
- if tok.Name.Local == "key" {
- if err := d.DecodeElement(&key, &tok); err != nil {
- return nil, err
- }
- hasKey = true
- } else if hasKey {
- var val string
- var err error
- switch n := tok.Name.Local; n {
- case "true", "false":
- // Bools are represented as and .
- val = n
- err = d.Skip()
- default:
- err = d.DecodeElement(&val, &tok)
- }
- if err != nil {
- return nil, err
- }
- values[key] = val
- hasKey = false
- } else {
- if err := d.Skip(); err != nil {
- return nil, err
- }
- }
- }
- }
- return values, nil
-}
-
-func uninstall(bundleID string) error {
- cmd := idevCmd(exec.Command(
- "ideviceinstaller",
- "-U", bundleID,
- ))
- if out, err := cmd.CombinedOutput(); err != nil {
- os.Stderr.Write(out)
- return fmt.Errorf("ideviceinstaller -U %q: %s", bundleID, err)
- }
- return nil
-}
-
-func install(appdir string) error {
- attempt := 0
- for {
- cmd := idevCmd(exec.Command(
- "ideviceinstaller",
- "-i", appdir,
- ))
- if out, err := cmd.CombinedOutput(); err != nil {
- // Sometimes, installing the app fails for some reason.
- // Give the device a few seconds and try again.
- if attempt < 5 {
- time.Sleep(5 * time.Second)
- attempt++
- continue
- }
- os.Stderr.Write(out)
- return fmt.Errorf("ideviceinstaller -i %q: %v (%d attempts)", appdir, err, attempt)
- }
- return nil
- }
-}
-
-func idevCmd(cmd *exec.Cmd) *exec.Cmd {
- if deviceID != "" {
- // Inject -u device_id after the executable, but before the arguments.
- args := []string{cmd.Args[0], "-u", deviceID}
- cmd.Args = append(args, cmd.Args[1:]...)
- }
- return cmd
-}
-
-func run(appdir, bundleID string, args []string) error {
- var env []string
- for _, e := range os.Environ() {
- // Don't override TMPDIR, HOME, GOCACHE on the device.
- if strings.HasPrefix(e, "TMPDIR=") || strings.HasPrefix(e, "HOME=") || strings.HasPrefix(e, "GOCACHE=") {
- continue
- }
- env = append(env, e)
- }
- attempt := 0
- for {
- // The device app path reported by the device might be stale, so retry
- // the lookup of the device path along with the lldb launching below.
- deviceapp, err := findDeviceAppPath(bundleID)
- if err != nil {
- // The device app path might not yet exist for a newly installed app.
- if attempt == 5 {
- return err
- }
- attempt++
- time.Sleep(5 * time.Second)
- continue
- }
- lldb := exec.Command(
- "python",
- "-", // Read script from stdin.
- appdir,
- deviceapp,
- )
- lldb.Args = append(lldb.Args, args...)
- lldb.Env = env
- lldb.Stdin = strings.NewReader(lldbDriver)
- lldb.Stdout = os.Stdout
- var out bytes.Buffer
- lldb.Stderr = io.MultiWriter(&out, os.Stderr)
- err = lldb.Start()
- if err == nil {
- // Forward SIGQUIT to the lldb driver which in turn will forward
- // to the running program.
- sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGQUIT)
- proc := lldb.Process
- go func() {
- for sig := range sigs {
- proc.Signal(sig)
- }
- }()
- err = lldb.Wait()
- signal.Stop(sigs)
- close(sigs)
- }
- // If the program was not started it can be retried without papering over
- // real test failures.
- started := bytes.HasPrefix(out.Bytes(), []byte("lldb: running program"))
- if started || err == nil || attempt == 5 {
- return err
- }
- // Sometimes, the app was not yet ready to launch or the device path was
- // stale. Retry.
- attempt++
- time.Sleep(5 * time.Second)
- }
-}
-
-func copyLocalDir(dst, src string) error {
- if err := os.Mkdir(dst, 0755); err != nil {
- return err
- }
-
- d, err := os.Open(src)
- if err != nil {
- return err
- }
- defer d.Close()
- fi, err := d.Readdir(-1)
- if err != nil {
- return err
- }
-
- for _, f := range fi {
- if f.IsDir() {
- if f.Name() == "testdata" {
- if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
- return err
- }
- }
- continue
- }
- if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
- return err
- }
- }
- return nil
-}
-
-func cp(dst, src string) error {
- out, err := exec.Command("cp", "-a", src, dst).CombinedOutput()
- if err != nil {
- os.Stderr.Write(out)
- }
- return err
-}
-
-func copyLocalData(dstbase string) (pkgpath string, err error) {
- cwd, err := os.Getwd()
- if err != nil {
- return "", err
- }
-
- finalPkgpath, underGoRoot, err := subdir()
- if err != nil {
- return "", err
- }
- cwd = strings.TrimSuffix(cwd, finalPkgpath)
-
- // Copy all immediate files and testdata directories between
- // the package being tested and the source root.
- pkgpath = ""
- for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
- if debug {
- log.Printf("copying %s", pkgpath)
- }
- pkgpath = filepath.Join(pkgpath, element)
- dst := filepath.Join(dstbase, pkgpath)
- src := filepath.Join(cwd, pkgpath)
- if err := copyLocalDir(dst, src); err != nil {
- return "", err
- }
- }
-
- if underGoRoot {
- // Copy timezone file.
- //
- // Typical apps have the zoneinfo.zip in the root of their app bundle,
- // read by the time package as the working directory at initialization.
- // As we move the working directory to the GOROOT pkg directory, we
- // install the zoneinfo.zip file in the pkgpath.
- err := cp(
- filepath.Join(dstbase, pkgpath),
- filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
- )
- if err != nil {
- return "", err
- }
- // Copy src/runtime/textflag.h for (at least) Test386EndToEnd in
- // cmd/asm/internal/asm.
- runtimePath := filepath.Join(dstbase, "src", "runtime")
- if err := os.MkdirAll(runtimePath, 0755); err != nil {
- return "", err
- }
- err = cp(
- filepath.Join(runtimePath, "textflag.h"),
- filepath.Join(cwd, "src", "runtime", "textflag.h"),
- )
- if err != nil {
- return "", err
- }
- }
-
- return finalPkgpath, nil
-}
-
-// subdir determines the package based on the current working directory,
-// and returns the path to the package source relative to $GOROOT (or $GOPATH).
-func subdir() (pkgpath string, underGoRoot bool, err error) {
- cwd, err := os.Getwd()
- if err != nil {
- return "", false, err
- }
- cwd, err = filepath.EvalSymlinks(cwd)
- if err != nil {
- log.Fatal(err)
- }
- goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
- if err != nil {
- return "", false, err
- }
- if strings.HasPrefix(cwd, goroot) {
- subdir, err := filepath.Rel(goroot, cwd)
- if err != nil {
- return "", false, err
- }
- return subdir, true, nil
- }
-
- for _, p := range filepath.SplitList(build.Default.GOPATH) {
- pabs, err := filepath.EvalSymlinks(p)
- if err != nil {
- return "", false, err
- }
- if !strings.HasPrefix(cwd, pabs) {
- continue
- }
- subdir, err := filepath.Rel(pabs, cwd)
- if err == nil {
- return subdir, false, nil
- }
- }
- return "", false, fmt.Errorf(
- "working directory %q is not in either GOROOT(%q) or GOPATH(%q)",
- cwd,
- runtime.GOROOT(),
- build.Default.GOPATH,
- )
-}
-
-func infoPlist(pkgpath string) string {
- return `
-
-
-
-CFBundleNamegolang.gotest
-CFBundleSupportedPlatformsiPhoneOS
-CFBundleExecutablegotest
-CFBundleVersion1.0
-CFBundleIdentifier` + bundleID + `
-CFBundleResourceSpecificationResourceRules.plist
-LSRequiresIPhoneOS
-CFBundleDisplayNamegotest
-GoExecWrapperWorkingDirectory` + pkgpath + `
-
-
-`
-}
-
-func entitlementsPlist() string {
- return `
-
-
-
- keychain-access-groups
- ` + appID + `
- get-task-allow
-
- application-identifier
- ` + appID + `
- com.apple.developer.team-identifier
- ` + teamID + `
-
-
-`
-}
-
-const resourceRules = `
-
-
-
- rules
-
- .*
-
- Info.plist
-
- omit
-
- weight
- 10
-
- ResourceRules.plist
-
- omit
-
- weight
- 100
-
-
-
-
-`
-
-const lldbDriver = `
-import sys
-import os
-import signal
-
-exe, device_exe, args = sys.argv[1], sys.argv[2], sys.argv[3:]
-
-env = []
-for k, v in os.environ.items():
- env.append(k + "=" + v)
-
-sys.path.append('/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python')
-
-import lldb
-
-debugger = lldb.SBDebugger.Create()
-debugger.SetAsync(True)
-debugger.SkipLLDBInitFiles(True)
-
-err = lldb.SBError()
-target = debugger.CreateTarget(exe, None, 'remote-ios', True, err)
-if not target.IsValid() or not err.Success():
- sys.stderr.write("lldb: failed to setup up target: %s\n" % (err))
- sys.exit(1)
-
-target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_exe))
-
-listener = debugger.GetListener()
-process = target.ConnectRemote(listener, 'connect://localhost:3222', None, err)
-if not err.Success():
- sys.stderr.write("lldb: failed to connect to remote target: %s\n" % (err))
- sys.exit(1)
-
-# Don't stop on signals.
-sigs = process.GetUnixSignals()
-for i in range(0, sigs.GetNumSignals()):
- sig = sigs.GetSignalAtIndex(i)
- sigs.SetShouldStop(sig, False)
- sigs.SetShouldNotify(sig, False)
-
-event = lldb.SBEvent()
-running = False
-prev_handler = None
-while True:
- if not listener.WaitForEvent(1, event):
- continue
- if not lldb.SBProcess.EventIsProcessEvent(event):
- continue
- if running:
- # Pass through stdout and stderr.
- while True:
- out = process.GetSTDOUT(8192)
- if not out:
- break
- sys.stdout.write(out)
- while True:
- out = process.GetSTDERR(8192)
- if not out:
- break
- sys.stderr.write(out)
- state = process.GetStateFromEvent(event)
- if state in [lldb.eStateCrashed, lldb.eStateDetached, lldb.eStateUnloaded, lldb.eStateExited]:
- if running:
- signal.signal(signal.SIGQUIT, prev_handler)
- break
- elif state == lldb.eStateConnected:
- process.RemoteLaunch(args, env, None, None, None, None, 0, False, err)
- if not err.Success():
- sys.stderr.write("lldb: failed to launch remote process: %s\n" % (err))
- process.Kill()
- debugger.Terminate()
- sys.exit(1)
- # Forward SIGQUIT to the program.
- def signal_handler(signal, frame):
- process.Signal(signal)
- prev_handler = signal.signal(signal.SIGQUIT, signal_handler)
- # Tell the Go driver that the program is running and should not be retried.
- sys.stderr.write("lldb: running program\n")
- running = True
- # Process stops once at the beginning. Continue.
- process.Continue()
-
-exitStatus = process.GetExitStatus()
-process.Kill()
-debugger.Terminate()
-sys.exit(exitStatus)
-`
diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go
new file mode 100644
index 0000000000..063c19ec58
--- /dev/null
+++ b/misc/ios/go_ios_exec.go
@@ -0,0 +1,912 @@
+// 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.
+
+// This program can be used as go_ios_$GOARCH_exec by the Go tool.
+// It executes binaries on an iOS device using the XCode toolchain
+// and the ios-deploy program: https://github.com/phonegap/ios-deploy
+//
+// This script supports an extra flag, -lldb, that pauses execution
+// just before the main program begins and allows the user to control
+// the remote lldb session. This flag is appended to the end of the
+// script's arguments and is not passed through to the underlying
+// binary.
+//
+// This script requires that three environment variables be set:
+// GOIOS_DEV_ID: The codesigning developer id or certificate identifier
+// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids.
+// GOIOS_TEAM_ID: The team id that owns the app id prefix.
+// $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these.
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+)
+
+const debug = false
+
+var tmpdir string
+
+var (
+ devID string
+ appID string
+ teamID string
+ bundleID string
+ deviceID string
+)
+
+// lock is a file lock to serialize iOS runs. It is global to avoid the
+// garbage collector finalizing it, closing the file and releasing the
+// lock prematurely.
+var lock *os.File
+
+func main() {
+ log.SetFlags(0)
+ log.SetPrefix("go_darwin_arm_exec: ")
+ if debug {
+ log.Println(strings.Join(os.Args, " "))
+ }
+ if len(os.Args) < 2 {
+ log.Fatal("usage: go_darwin_arm_exec a.out")
+ }
+
+ // For compatibility with the old builders, use a fallback bundle ID
+ bundleID = "golang.gotest"
+
+ exitCode, err := runMain()
+ if err != nil {
+ log.Fatalf("%v\n", err)
+ }
+ os.Exit(exitCode)
+}
+
+func runMain() (int, error) {
+ var err error
+ tmpdir, err = ioutil.TempDir("", "go_darwin_arm_exec_")
+ if err != nil {
+ return 1, err
+ }
+ if !debug {
+ defer os.RemoveAll(tmpdir)
+ }
+
+ appdir := filepath.Join(tmpdir, "gotest.app")
+ os.RemoveAll(appdir)
+
+ if err := assembleApp(appdir, os.Args[1]); err != nil {
+ return 1, err
+ }
+
+ // This wrapper uses complicated machinery to run iOS binaries. It
+ // works, but only when running one binary at a time.
+ // Use a file lock to make sure only one wrapper is running at a time.
+ //
+ // The lock file is never deleted, to avoid concurrent locks on distinct
+ // files with the same path.
+ lockName := filepath.Join(os.TempDir(), "go_darwin_arm_exec-"+deviceID+".lock")
+ lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666)
+ if err != nil {
+ return 1, err
+ }
+ if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
+ return 1, err
+ }
+
+ if goarch := os.Getenv("GOARCH"); goarch == "arm64" {
+ err = runOnDevice(appdir)
+ } else {
+ err = runOnSimulator(appdir)
+ }
+ if err != nil {
+ // If the lldb driver completed with an exit code, use that.
+ if err, ok := err.(*exec.ExitError); ok {
+ if ws, ok := err.Sys().(interface{ ExitStatus() int }); ok {
+ return ws.ExitStatus(), nil
+ }
+ }
+ return 1, err
+ }
+ return 0, nil
+}
+
+func runOnSimulator(appdir string) error {
+ if err := installSimulator(appdir); err != nil {
+ return err
+ }
+
+ return runSimulator(appdir, bundleID, os.Args[2:])
+}
+
+func runOnDevice(appdir string) error {
+ // e.g. B393DDEB490947F5A463FD074299B6C0AXXXXXXX
+ devID = getenv("GOIOS_DEV_ID")
+
+ // e.g. Z8B3JBXXXX.org.golang.sample, Z8B3JBXXXX prefix is available at
+ // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID.
+ appID = getenv("GOIOS_APP_ID")
+
+ // e.g. Z8B3JBXXXX, available at
+ // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID.
+ teamID = getenv("GOIOS_TEAM_ID")
+
+ // Device IDs as listed with ios-deploy -c.
+ deviceID = os.Getenv("GOIOS_DEVICE_ID")
+
+ parts := strings.SplitN(appID, ".", 2)
+ if len(parts) == 2 {
+ bundleID = parts[1]
+ }
+
+ if err := signApp(appdir); err != nil {
+ return err
+ }
+
+ if err := uninstallDevice(bundleID); err != nil {
+ return err
+ }
+
+ if err := installDevice(appdir); err != nil {
+ return err
+ }
+
+ if err := mountDevImage(); err != nil {
+ return err
+ }
+
+ // Kill any hanging debug bridges that might take up port 3222.
+ exec.Command("killall", "idevicedebugserverproxy").Run()
+
+ closer, err := startDebugBridge()
+ if err != nil {
+ return err
+ }
+ defer closer()
+
+ return runDevice(appdir, bundleID, os.Args[2:])
+}
+
+func getenv(envvar string) string {
+ s := os.Getenv(envvar)
+ if s == "" {
+ log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar)
+ }
+ return s
+}
+
+func assembleApp(appdir, bin string) error {
+ if err := os.MkdirAll(appdir, 0755); err != nil {
+ return err
+ }
+
+ if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil {
+ return err
+ }
+
+ pkgpath, err := copyLocalData(appdir)
+ if err != nil {
+ return err
+ }
+
+ entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
+ if err := ioutil.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil {
+ return err
+ }
+ return nil
+}
+
+func signApp(appdir string) error {
+ entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
+ cmd := exec.Command(
+ "codesign",
+ "-f",
+ "-s", devID,
+ "--entitlements", entitlementsPath,
+ appdir,
+ )
+ if debug {
+ log.Println(strings.Join(cmd.Args, " "))
+ }
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("codesign: %v", err)
+ }
+ return nil
+}
+
+// mountDevImage ensures a developer image is mounted on the device.
+// The image contains the device lldb server for idevicedebugserverproxy
+// to connect to.
+func mountDevImage() error {
+ // Check for existing mount.
+ cmd := idevCmd(exec.Command("ideviceimagemounter", "-l", "-x"))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ os.Stderr.Write(out)
+ return fmt.Errorf("ideviceimagemounter: %v", err)
+ }
+ var info struct {
+ Dict struct {
+ Data []byte `xml:",innerxml"`
+ } `xml:"dict"`
+ }
+ if err := xml.Unmarshal(out, &info); err != nil {
+ return fmt.Errorf("mountDevImage: failed to decode mount information: %v", err)
+ }
+ dict, err := parsePlistDict(info.Dict.Data)
+ if err != nil {
+ return fmt.Errorf("mountDevImage: failed to parse mount information: %v", err)
+ }
+ if dict["ImagePresent"] == "true" && dict["Status"] == "Complete" {
+ return nil
+ }
+ // Some devices only give us an ImageSignature key.
+ if _, exists := dict["ImageSignature"]; exists {
+ return nil
+ }
+ // No image is mounted. Find a suitable image.
+ imgPath, err := findDevImage()
+ if err != nil {
+ return err
+ }
+ sigPath := imgPath + ".signature"
+ cmd = idevCmd(exec.Command("ideviceimagemounter", imgPath, sigPath))
+ if out, err := cmd.CombinedOutput(); err != nil {
+ os.Stderr.Write(out)
+ return fmt.Errorf("ideviceimagemounter: %v", err)
+ }
+ return nil
+}
+
+// findDevImage use the device iOS version and build to locate a suitable
+// developer image.
+func findDevImage() (string, error) {
+ cmd := idevCmd(exec.Command("ideviceinfo"))
+ out, err := cmd.Output()
+ if err != nil {
+ return "", fmt.Errorf("ideviceinfo: %v", err)
+ }
+ var iosVer, buildVer string
+ lines := bytes.Split(out, []byte("\n"))
+ for _, line := range lines {
+ spl := bytes.SplitN(line, []byte(": "), 2)
+ if len(spl) != 2 {
+ continue
+ }
+ key, val := string(spl[0]), string(spl[1])
+ switch key {
+ case "ProductVersion":
+ iosVer = val
+ case "BuildVersion":
+ buildVer = val
+ }
+ }
+ if iosVer == "" || buildVer == "" {
+ return "", errors.New("failed to parse ideviceinfo output")
+ }
+ verSplit := strings.Split(iosVer, ".")
+ if len(verSplit) > 2 {
+ // Developer images are specific to major.minor ios version.
+ // Cut off the patch version.
+ iosVer = strings.Join(verSplit[:2], ".")
+ }
+ sdkBase := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"
+ patterns := []string{fmt.Sprintf("%s (%s)", iosVer, buildVer), fmt.Sprintf("%s (*)", iosVer), fmt.Sprintf("%s*", iosVer)}
+ for _, pattern := range patterns {
+ matches, err := filepath.Glob(filepath.Join(sdkBase, pattern, "DeveloperDiskImage.dmg"))
+ if err != nil {
+ return "", fmt.Errorf("findDevImage: %v", err)
+ }
+ if len(matches) > 0 {
+ return matches[0], nil
+ }
+ }
+ return "", fmt.Errorf("failed to find matching developer image for iOS version %s build %s", iosVer, buildVer)
+}
+
+// startDebugBridge ensures that the idevicedebugserverproxy runs on
+// port 3222.
+func startDebugBridge() (func(), error) {
+ errChan := make(chan error, 1)
+ cmd := idevCmd(exec.Command("idevicedebugserverproxy", "3222"))
+ var stderr bytes.Buffer
+ cmd.Stderr = &stderr
+ if err := cmd.Start(); err != nil {
+ return nil, fmt.Errorf("idevicedebugserverproxy: %v", err)
+ }
+ go func() {
+ if err := cmd.Wait(); err != nil {
+ if _, ok := err.(*exec.ExitError); ok {
+ errChan <- fmt.Errorf("idevicedebugserverproxy: %s", stderr.Bytes())
+ } else {
+ errChan <- fmt.Errorf("idevicedebugserverproxy: %v", err)
+ }
+ }
+ errChan <- nil
+ }()
+ closer := func() {
+ cmd.Process.Kill()
+ <-errChan
+ }
+ // Dial localhost:3222 to ensure the proxy is ready.
+ delay := time.Second / 4
+ for attempt := 0; attempt < 5; attempt++ {
+ conn, err := net.DialTimeout("tcp", "localhost:3222", 5*time.Second)
+ if err == nil {
+ conn.Close()
+ return closer, nil
+ }
+ select {
+ case <-time.After(delay):
+ delay *= 2
+ case err := <-errChan:
+ return nil, err
+ }
+ }
+ closer()
+ return nil, errors.New("failed to set up idevicedebugserverproxy")
+}
+
+// findDeviceAppPath returns the device path to the app with the
+// given bundle ID. It parses the output of ideviceinstaller -l -o xml,
+// looking for the bundle ID and the corresponding Path value.
+func findDeviceAppPath(bundleID string) (string, error) {
+ cmd := idevCmd(exec.Command("ideviceinstaller", "-l", "-o", "xml"))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ os.Stderr.Write(out)
+ return "", fmt.Errorf("ideviceinstaller: -l -o xml %v", err)
+ }
+ var list struct {
+ Apps []struct {
+ Data []byte `xml:",innerxml"`
+ } `xml:"array>dict"`
+ }
+ if err := xml.Unmarshal(out, &list); err != nil {
+ return "", fmt.Errorf("failed to parse ideviceinstaller output: %v", err)
+ }
+ for _, app := range list.Apps {
+ values, err := parsePlistDict(app.Data)
+ if err != nil {
+ return "", fmt.Errorf("findDeviceAppPath: failed to parse app dict: %v", err)
+ }
+ if values["CFBundleIdentifier"] == bundleID {
+ if path, ok := values["Path"]; ok {
+ return path, nil
+ }
+ }
+ }
+ return "", fmt.Errorf("failed to find device path for bundle: %s", bundleID)
+}
+
+// Parse an xml encoded plist. Plist values are mapped to string.
+func parsePlistDict(dict []byte) (map[string]string, error) {
+ d := xml.NewDecoder(bytes.NewReader(dict))
+ values := make(map[string]string)
+ var key string
+ var hasKey bool
+ for {
+ tok, err := d.Token()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+ if tok, ok := tok.(xml.StartElement); ok {
+ if tok.Name.Local == "key" {
+ if err := d.DecodeElement(&key, &tok); err != nil {
+ return nil, err
+ }
+ hasKey = true
+ } else if hasKey {
+ var val string
+ var err error
+ switch n := tok.Name.Local; n {
+ case "true", "false":
+ // Bools are represented as and .
+ val = n
+ err = d.Skip()
+ default:
+ err = d.DecodeElement(&val, &tok)
+ }
+ if err != nil {
+ return nil, err
+ }
+ values[key] = val
+ hasKey = false
+ } else {
+ if err := d.Skip(); err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+ return values, nil
+}
+
+func installSimulator(appdir string) error {
+ cmd := exec.Command(
+ "xcrun", "simctl", "install",
+ "booted", // Install to the booted simulator.
+ appdir,
+ )
+ if out, err := cmd.CombinedOutput(); err != nil {
+ os.Stderr.Write(out)
+ return fmt.Errorf("xcrun simctl install booted %q: %v", appdir, err)
+ }
+ return nil
+}
+
+func uninstallDevice(bundleID string) error {
+ cmd := idevCmd(exec.Command(
+ "ideviceinstaller",
+ "-U", bundleID,
+ ))
+ if out, err := cmd.CombinedOutput(); err != nil {
+ os.Stderr.Write(out)
+ return fmt.Errorf("ideviceinstaller -U %q: %s", bundleID, err)
+ }
+ return nil
+}
+
+func installDevice(appdir string) error {
+ attempt := 0
+ for {
+ cmd := idevCmd(exec.Command(
+ "ideviceinstaller",
+ "-i", appdir,
+ ))
+ if out, err := cmd.CombinedOutput(); err != nil {
+ // Sometimes, installing the app fails for some reason.
+ // Give the device a few seconds and try again.
+ if attempt < 5 {
+ time.Sleep(5 * time.Second)
+ attempt++
+ continue
+ }
+ os.Stderr.Write(out)
+ return fmt.Errorf("ideviceinstaller -i %q: %v (%d attempts)", appdir, err, attempt)
+ }
+ return nil
+ }
+}
+
+func idevCmd(cmd *exec.Cmd) *exec.Cmd {
+ if deviceID != "" {
+ // Inject -u device_id after the executable, but before the arguments.
+ args := []string{cmd.Args[0], "-u", deviceID}
+ cmd.Args = append(args, cmd.Args[1:]...)
+ }
+ return cmd
+}
+
+func runSimulator(appdir, bundleID string, args []string) error {
+ cmd := exec.Command(
+ "xcrun", "simctl", "launch",
+ "--wait-for-debugger",
+ "booted",
+ bundleID,
+ )
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ os.Stderr.Write(out)
+ return fmt.Errorf("xcrun simctl launch booted %q: %v", bundleID, err)
+ }
+ var processID int
+ var ignore string
+ if _, err := fmt.Sscanf(string(out), "%s %d", &ignore, &processID); err != nil {
+ return fmt.Errorf("runSimulator: couldn't find processID from `simctl launch`: %v (%q)", err, out)
+ }
+ _, err = runLLDB("ios-simulator", appdir, strconv.Itoa(processID), args)
+ return err
+}
+
+func runDevice(appdir, bundleID string, args []string) error {
+ attempt := 0
+ for {
+ // The device app path reported by the device might be stale, so retry
+ // the lookup of the device path along with the lldb launching below.
+ deviceapp, err := findDeviceAppPath(bundleID)
+ if err != nil {
+ // The device app path might not yet exist for a newly installed app.
+ if attempt == 5 {
+ return err
+ }
+ attempt++
+ time.Sleep(5 * time.Second)
+ continue
+ }
+ out, err := runLLDB("remote-ios", appdir, deviceapp, args)
+ // If the program was not started it can be retried without papering over
+ // real test failures.
+ started := bytes.HasPrefix(out, []byte("lldb: running program"))
+ if started || err == nil || attempt == 5 {
+ return err
+ }
+ // Sometimes, the app was not yet ready to launch or the device path was
+ // stale. Retry.
+ attempt++
+ time.Sleep(5 * time.Second)
+ }
+}
+
+func runLLDB(target, appdir, deviceapp string, args []string) ([]byte, error) {
+ var env []string
+ for _, e := range os.Environ() {
+ // Don't override TMPDIR, HOME, GOCACHE on the device.
+ if strings.HasPrefix(e, "TMPDIR=") || strings.HasPrefix(e, "HOME=") || strings.HasPrefix(e, "GOCACHE=") {
+ continue
+ }
+ env = append(env, e)
+ }
+ lldb := exec.Command(
+ "python",
+ "-", // Read script from stdin.
+ target,
+ appdir,
+ deviceapp,
+ )
+ lldb.Args = append(lldb.Args, args...)
+ lldb.Env = env
+ lldb.Stdin = strings.NewReader(lldbDriver)
+ lldb.Stdout = os.Stdout
+ var out bytes.Buffer
+ lldb.Stderr = io.MultiWriter(&out, os.Stderr)
+ err := lldb.Start()
+ if err == nil {
+ // Forward SIGQUIT to the lldb driver which in turn will forward
+ // to the running program.
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGQUIT)
+ proc := lldb.Process
+ go func() {
+ for sig := range sigs {
+ proc.Signal(sig)
+ }
+ }()
+ err = lldb.Wait()
+ signal.Stop(sigs)
+ close(sigs)
+ }
+ return out.Bytes(), err
+}
+
+func copyLocalDir(dst, src string) error {
+ if err := os.Mkdir(dst, 0755); err != nil {
+ return err
+ }
+
+ d, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer d.Close()
+ fi, err := d.Readdir(-1)
+ if err != nil {
+ return err
+ }
+
+ for _, f := range fi {
+ if f.IsDir() {
+ if f.Name() == "testdata" {
+ if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func cp(dst, src string) error {
+ out, err := exec.Command("cp", "-a", src, dst).CombinedOutput()
+ if err != nil {
+ os.Stderr.Write(out)
+ }
+ return err
+}
+
+func copyLocalData(dstbase string) (pkgpath string, err error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+
+ finalPkgpath, underGoRoot, err := subdir()
+ if err != nil {
+ return "", err
+ }
+ cwd = strings.TrimSuffix(cwd, finalPkgpath)
+
+ // Copy all immediate files and testdata directories between
+ // the package being tested and the source root.
+ pkgpath = ""
+ for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
+ if debug {
+ log.Printf("copying %s", pkgpath)
+ }
+ pkgpath = filepath.Join(pkgpath, element)
+ dst := filepath.Join(dstbase, pkgpath)
+ src := filepath.Join(cwd, pkgpath)
+ if err := copyLocalDir(dst, src); err != nil {
+ return "", err
+ }
+ }
+
+ if underGoRoot {
+ // Copy timezone file.
+ //
+ // Typical apps have the zoneinfo.zip in the root of their app bundle,
+ // read by the time package as the working directory at initialization.
+ // As we move the working directory to the GOROOT pkg directory, we
+ // install the zoneinfo.zip file in the pkgpath.
+ err := cp(
+ filepath.Join(dstbase, pkgpath),
+ filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
+ )
+ if err != nil {
+ return "", err
+ }
+ // Copy src/runtime/textflag.h for (at least) Test386EndToEnd in
+ // cmd/asm/internal/asm.
+ runtimePath := filepath.Join(dstbase, "src", "runtime")
+ if err := os.MkdirAll(runtimePath, 0755); err != nil {
+ return "", err
+ }
+ err = cp(
+ filepath.Join(runtimePath, "textflag.h"),
+ filepath.Join(cwd, "src", "runtime", "textflag.h"),
+ )
+ if err != nil {
+ return "", err
+ }
+ }
+
+ return finalPkgpath, nil
+}
+
+// subdir determines the package based on the current working directory,
+// and returns the path to the package source relative to $GOROOT (or $GOPATH).
+func subdir() (pkgpath string, underGoRoot bool, err error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", false, err
+ }
+ cwd, err = filepath.EvalSymlinks(cwd)
+ if err != nil {
+ log.Fatal(err)
+ }
+ goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
+ if err != nil {
+ return "", false, err
+ }
+ if strings.HasPrefix(cwd, goroot) {
+ subdir, err := filepath.Rel(goroot, cwd)
+ if err != nil {
+ return "", false, err
+ }
+ return subdir, true, nil
+ }
+
+ for _, p := range filepath.SplitList(build.Default.GOPATH) {
+ pabs, err := filepath.EvalSymlinks(p)
+ if err != nil {
+ return "", false, err
+ }
+ if !strings.HasPrefix(cwd, pabs) {
+ continue
+ }
+ subdir, err := filepath.Rel(pabs, cwd)
+ if err == nil {
+ return subdir, false, nil
+ }
+ }
+ return "", false, fmt.Errorf(
+ "working directory %q is not in either GOROOT(%q) or GOPATH(%q)",
+ cwd,
+ runtime.GOROOT(),
+ build.Default.GOPATH,
+ )
+}
+
+func infoPlist(pkgpath string) string {
+ return `
+
+
+
+CFBundleNamegolang.gotest
+CFBundleSupportedPlatformsiPhoneOS
+CFBundleExecutablegotest
+CFBundleVersion1.0
+CFBundleShortVersionString1.0
+CFBundleIdentifier` + bundleID + `
+CFBundleResourceSpecificationResourceRules.plist
+LSRequiresIPhoneOS
+CFBundleDisplayNamegotest
+GoExecWrapperWorkingDirectory` + pkgpath + `
+
+
+`
+}
+
+func entitlementsPlist() string {
+ return `
+
+
+
+ keychain-access-groups
+ ` + appID + `
+ get-task-allow
+
+ application-identifier
+ ` + appID + `
+ com.apple.developer.team-identifier
+ ` + teamID + `
+
+
+`
+}
+
+const resourceRules = `
+
+
+
+ rules
+
+ .*
+
+ Info.plist
+
+ omit
+
+ weight
+ 10
+
+ ResourceRules.plist
+
+ omit
+
+ weight
+ 100
+
+
+
+
+`
+
+const lldbDriver = `
+import sys
+import os
+import signal
+
+platform, exe, device_exe_or_pid, args = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4:]
+
+env = []
+for k, v in os.environ.items():
+ env.append(k + "=" + v)
+
+sys.path.append('/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python')
+
+import lldb
+
+debugger = lldb.SBDebugger.Create()
+debugger.SetAsync(True)
+debugger.SkipLLDBInitFiles(True)
+
+err = lldb.SBError()
+target = debugger.CreateTarget(exe, None, platform, True, err)
+if not target.IsValid() or not err.Success():
+ sys.stderr.write("lldb: failed to setup up target: %s\n" % (err))
+ sys.exit(1)
+
+listener = debugger.GetListener()
+
+if platform == 'remote-ios':
+ target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_exe_or_pid))
+ process = target.ConnectRemote(listener, 'connect://localhost:3222', None, err)
+else:
+ process = target.AttachToProcessWithID(listener, int(device_exe_or_pid), err)
+
+if not err.Success():
+ sys.stderr.write("lldb: failed to connect to remote target %s: %s\n" % (device_exe_or_pid, err))
+ sys.exit(1)
+
+# Don't stop on signals.
+sigs = process.GetUnixSignals()
+for i in range(0, sigs.GetNumSignals()):
+ sig = sigs.GetSignalAtIndex(i)
+ sigs.SetShouldStop(sig, False)
+ sigs.SetShouldNotify(sig, False)
+
+event = lldb.SBEvent()
+running = False
+prev_handler = None
+
+def signal_handler(signal, frame):
+ process.Signal(signal)
+
+def run_program():
+ # Forward SIGQUIT to the program.
+ prev_handler = signal.signal(signal.SIGQUIT, signal_handler)
+ # Tell the Go driver that the program is running and should not be retried.
+ sys.stderr.write("lldb: running program\n")
+ running = True
+ # Process is stopped at attach/launch. Let it run.
+ process.Continue()
+
+if platform != 'remote-ios':
+ # For the local emulator the program is ready to run.
+ # For remote device runs, we need to wait for eStateConnected,
+ # below.
+ run_program()
+
+while True:
+ if not listener.WaitForEvent(1, event):
+ continue
+ if not lldb.SBProcess.EventIsProcessEvent(event):
+ continue
+ if running:
+ # Pass through stdout and stderr.
+ while True:
+ out = process.GetSTDOUT(8192)
+ if not out:
+ break
+ sys.stdout.write(out)
+ while True:
+ out = process.GetSTDERR(8192)
+ if not out:
+ break
+ sys.stderr.write(out)
+ state = process.GetStateFromEvent(event)
+ if state in [lldb.eStateCrashed, lldb.eStateDetached, lldb.eStateUnloaded, lldb.eStateExited]:
+ if running:
+ signal.signal(signal.SIGQUIT, prev_handler)
+ break
+ elif state == lldb.eStateConnected:
+ if platform == 'remote-ios':
+ process.RemoteLaunch(args, env, None, None, None, None, 0, False, err)
+ if not err.Success():
+ sys.stderr.write("lldb: failed to launch remote process: %s\n" % (err))
+ process.Kill()
+ debugger.Terminate()
+ sys.exit(1)
+ run_program()
+
+exitStatus = process.GetExitStatus()
+exitDesc = process.GetExitDescription()
+process.Kill()
+debugger.Terminate()
+if exitStatus == 0 and exitDesc is not None:
+ # Ensure tests fail when killed by a signal.
+ exitStatus = 123
+
+sys.exit(exitStatus)
+`
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 5d62c1e8fa..3b3eb113b1 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -1453,7 +1453,7 @@ func wrapperPathFor(goos, goarch string) string {
}
case (goos == "darwin" || goos == "ios") && goarch == "arm64":
if gohostos != "darwin" || gohostarch != "arm64" {
- return pathf("%s/misc/ios/go_darwin_arm_exec.go", goroot)
+ return pathf("%s/misc/ios/go_ios_exec.go", goroot)
}
}
return ""
diff --git a/src/iostest.bash b/src/iostest.bash
index 5fa6744979..33b8c101ff 100755
--- a/src/iostest.bash
+++ b/src/iostest.bash
@@ -38,7 +38,7 @@ if [ "$1" = "-restart" ]; then
sleep 30
# Poll until the device has restarted.
until idevicediagnostics $IDEVARGS diagnostics; do
- # TODO(crawshaw): replace with a test app using go_darwin_arm_exec.
+ # TODO(crawshaw): replace with a test app using go_ios_exec.
echo "waiting for idevice to come online"
sleep 10
done
diff --git a/src/runtime/cgo/gcc_darwin_arm64.c b/src/runtime/cgo/gcc_darwin_arm64.c
index fd7d4084c9..9ea43ae4af 100644
--- a/src/runtime/cgo/gcc_darwin_arm64.c
+++ b/src/runtime/cgo/gcc_darwin_arm64.c
@@ -131,7 +131,7 @@ init_working_dir()
fprintf(stderr, "runtime/cgo: chdir(%s) failed\n", dir);
}
- // The test harness in go_darwin_arm_exec passes the relative working directory
+ // The test harness in go_ios_exec passes the relative working directory
// in the GoExecWrapperWorkingDirectory property of the app bundle.
CFStringRef wd_ref = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("GoExecWrapperWorkingDirectory"));
if (wd_ref != NULL) {
--
cgit v1.2.3-54-g00ecf
From 6f2c92e1e11b61dfede9ce861f2fe6217b5ce130 Mon Sep 17 00:00:00 2001
From: Elias Naur
Date: Sat, 3 Oct 2020 11:27:04 +0200
Subject: iostest.bash: remove
There are no tethered iOS builders left, and should they appear in
the future, they should use all.bash.
Change-Id: I3217789514ffa725e4d2584e4991d899c5fda995
Reviewed-on: https://go-review.googlesource.com/c/go/+/259278
Trust: Elias Naur
Reviewed-by: Dmitri Shuralyov
---
src/iostest.bash | 66 --------------------------------------------------------
1 file changed, 66 deletions(-)
delete mode 100755 src/iostest.bash
diff --git a/src/iostest.bash b/src/iostest.bash
deleted file mode 100755
index 33b8c101ff..0000000000
--- a/src/iostest.bash
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env bash
-# 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.
-
-# For testing darwin/arm64 on iOS.
-
-set -e
-ulimit -c 0 # no core files
-
-if [ ! -f make.bash ]; then
- echo 'iostest.bash must be run from $GOROOT/src' 1>&2
- exit 1
-fi
-
-if [ -z $GOOS ]; then
- export GOOS=darwin
-fi
-if [ "$GOOS" != "darwin" ]; then
- echo "iostest.bash requires GOOS=darwin, got GOOS=$GOOS" 1>&2
- exit 1
-fi
-if [ "$GOARCH" != "arm64" ]; then
- echo "iostest.bash requires GOARCH=arm64, got GOARCH=$GOARCH" 1>&2
- exit 1
-fi
-
-if [ "$1" = "-restart" ]; then
- # Reboot to make sure previous runs do not interfere with the current run.
- # It is reasonably easy for a bad program leave an iOS device in an
- # almost unusable state.
- IDEVARGS=
- if [ -n "$GOIOS_DEVICE_ID" ]; then
- IDEVARGS="-u $GOIOS_DEVICE_ID"
- fi
- idevicediagnostics $IDEVARGS restart
- # Initial sleep to make sure we are restarting before we start polling.
- sleep 30
- # Poll until the device has restarted.
- until idevicediagnostics $IDEVARGS diagnostics; do
- # TODO(crawshaw): replace with a test app using go_ios_exec.
- echo "waiting for idevice to come online"
- sleep 10
- done
- # Diagnostics are reported during boot before the device can start an
- # app. Wait a little longer before trying to use the device.
- sleep 30
-fi
-
-unset GOBIN
-export GOROOT=$(dirname $(pwd))
-export PATH=$GOROOT/bin:$PATH
-export CGO_ENABLED=1
-export CC_FOR_TARGET=$GOROOT/misc/ios/clangwrap.sh
-
-# Run the build for the host bootstrap, so we can build detect.go.
-# Also lets us fail early before the (slow) ios-deploy if the build is broken.
-./make.bash
-
-if [ "$GOIOS_DEV_ID" = "" ]; then
- echo "detecting iOS development identity"
- eval $(GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH go run ../misc/ios/detect.go)
-fi
-
-# Run standard tests.
-bash run.bash --no-rebuild
--
cgit v1.2.3-54-g00ecf
From 059ca6185c19404e991cc7714b1df047fd78785f Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Fri, 2 Oct 2020 21:57:24 -0400
Subject: cmd/dist: detect gohostarch on ios/arm64
Add a case for gohostos == "ios" along with "darwin".
Updates #38485.
Change-Id: Ic7310e6c97d405f78a5e5db1a639860455e61327
Reviewed-on: https://go-review.googlesource.com/c/go/+/259337
Trust: Cherry Zhang
Run-TryBot: Cherry Zhang
TryBot-Result: Go Bot
Reviewed-by: Dmitri Shuralyov
---
src/cmd/dist/main.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go
index 224b6c0c3e..37fc522356 100644
--- a/src/cmd/dist/main.go
+++ b/src/cmd/dist/main.go
@@ -129,7 +129,7 @@ func main() {
gohostarch = "riscv64"
case strings.Contains(out, "s390x"):
gohostarch = "s390x"
- case gohostos == "darwin":
+ case gohostos == "darwin", gohostos == "ios":
if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM64_") {
gohostarch = "arm64"
}
--
cgit v1.2.3-54-g00ecf
From 39d562ecea74bb41aa8fbb9d016fa64165e84bb3 Mon Sep 17 00:00:00 2001
From: Elias Naur
Date: Mon, 5 Oct 2020 17:51:54 +0200
Subject: misc/ios: fixup review comments from CL 255257
Change-Id: I247fc9e0e26e706e6af07367f953eaa1b7e544c1
Reviewed-on: https://go-review.googlesource.com/c/go/+/259577
Trust: Elias Naur
Run-TryBot: Elias Naur
Reviewed-by: Dmitri Shuralyov
TryBot-Result: Go Bot
---
misc/ios/detect.go | 2 +-
misc/ios/go_ios_exec.go | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/misc/ios/detect.go b/misc/ios/detect.go
index b4651dfbb8..d32bcc3202 100644
--- a/misc/ios/detect.go
+++ b/misc/ios/detect.go
@@ -6,7 +6,7 @@
// detect attempts to autodetect the correct
// values of the environment variables
-// used by go_io_exec.
+// used by go_ios_exec.
// detect shells out to ideviceinfo, a third party program that can
// be obtained by following the instructions at
// https://github.com/libimobiledevice/libimobiledevice.
diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go
index 063c19ec58..0acf1b259c 100644
--- a/misc/ios/go_ios_exec.go
+++ b/misc/ios/go_ios_exec.go
@@ -59,12 +59,12 @@ var lock *os.File
func main() {
log.SetFlags(0)
- log.SetPrefix("go_darwin_arm_exec: ")
+ log.SetPrefix("go_ios_exec: ")
if debug {
log.Println(strings.Join(os.Args, " "))
}
if len(os.Args) < 2 {
- log.Fatal("usage: go_darwin_arm_exec a.out")
+ log.Fatal("usage: go_ios_exec a.out")
}
// For compatibility with the old builders, use a fallback bundle ID
@@ -79,7 +79,7 @@ func main() {
func runMain() (int, error) {
var err error
- tmpdir, err = ioutil.TempDir("", "go_darwin_arm_exec_")
+ tmpdir, err = ioutil.TempDir("", "go_ios_exec_")
if err != nil {
return 1, err
}
@@ -100,7 +100,7 @@ func runMain() (int, error) {
//
// The lock file is never deleted, to avoid concurrent locks on distinct
// files with the same path.
- lockName := filepath.Join(os.TempDir(), "go_darwin_arm_exec-"+deviceID+".lock")
+ lockName := filepath.Join(os.TempDir(), "go_ios_exec-"+deviceID+".lock")
lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666)
if err != nil {
return 1, err
--
cgit v1.2.3-54-g00ecf
From e70bbc702f093ab2d5e305ddb33b8dca2baf8104 Mon Sep 17 00:00:00 2001
From: Roland Bracewell Shoemaker
Date: Mon, 5 Oct 2020 15:46:23 +0000
Subject: encoding/asn1: clarify use of SET suffix
This change clarifies the usage of the SET type name suffix. Previously
the documentation was somewhat confusing about where the suffix should
be used, and when used what it applied to. For instance the previous
language could be interpreted such that []exampleSET would be parsed as
a SEQUENCE OF SET, which is incorrect as the SET suffix only applies to
slice types, such as type exampleSET []struct{} which is parsed as a
SET OF SEQUENCE.
Change-Id: I74201d9969f931f69391c236559f66cb460569ec
GitHub-Last-Rev: d0d2ddc587df4564a265c800efb9d8e204002624
GitHub-Pull-Request: golang/go#38543
Reviewed-on: https://go-review.googlesource.com/c/go/+/229078
Trust: Roland Shoemaker
Reviewed-by: Filippo Valsorda
---
src/encoding/asn1/asn1.go | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index fa3d4e327b..068594e2a1 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -1086,9 +1086,10 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
// If the type of the first field of a structure is RawContent then the raw
// ASN1 contents of the struct will be stored in it.
//
-// If the type name of a slice element ends with "SET" then it's treated as if
-// the "set" tag was set on it. This can be used with nested slices where a
-// struct tag cannot be given.
+// If the name of a slice type ends with "SET" then it's treated as if
+// the "set" tag was set on it. This results in interpreting the type as a
+// SET OF x rather than a SEQUENCE OF x. This can be used with nested slices
+// where a struct tag cannot be given.
//
// Other ASN.1 types are not supported; if it encounters them,
// Unmarshal returns a parse error.
--
cgit v1.2.3-54-g00ecf
From 9dc65d7dc9268d5150174ec55cc4753fe18f554c Mon Sep 17 00:00:00 2001
From: Austin Clements
Date: Sat, 3 Oct 2020 16:44:22 -0400
Subject: runtime: correct signature of call16
The signature of call16 is currently missing the "typ" parameter. This
CL fixes this. This wasn't caught by vet because call16 is defined by
macro expansion (see #17544), and we didn't notice the mismatch with
the other call* functions because call16 is defined only on 32-bit
architectures and lives alone in stubs32.go.
Unfortunately, this means its GC signature is also wrong: the "arg"
parameter is treated as a scalar rather than a pointer, so GC won't
trace it and stack copying won't adjust it. This turns out to matter
in exactly one case right now: on 32-bit architectures (which are the
only architectures where call16 is defined), a stack-allocated defer
of a function with a 16-byte or smaller argument frame including a
non-empty result area can corrupt memory if the deferred function
grows the stack and is invoked during a panic. Whew. All other current
uses of reflectcall pass a heap-allocated "arg" frame (which happens
to be reachable from other stack roots, so tracing isn't a problem).
Curiously, in 2016, the signatures of all call* functions were wrong
in exactly this way. CL 31654 fixed all of them in stubs.go, but
missed the one in stubs32.go.
Fixes #41795.
Change-Id: I31e3c0df201f79ee5707eeb8dc4ff0d13fc10ada
Reviewed-on: https://go-review.googlesource.com/c/go/+/259338
Trust: Austin Clements
Run-TryBot: Austin Clements
TryBot-Result: Go Bot
Reviewed-by: Cherry Zhang
---
src/runtime/stubs32.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/runtime/stubs32.go b/src/runtime/stubs32.go
index a7f52f6b9e..c4715fe989 100644
--- a/src/runtime/stubs32.go
+++ b/src/runtime/stubs32.go
@@ -11,4 +11,4 @@ import "unsafe"
// Declarations for runtime services implemented in C or assembly that
// are only present on 32 bit systems.
-func call16(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call16(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
--
cgit v1.2.3-54-g00ecf
From 40bff82885b8de850f909f38357c53670562f815 Mon Sep 17 00:00:00 2001
From: Austin Clements
Date: Sat, 3 Oct 2020 20:40:49 -0400
Subject: runtime: define and use call16 everywhere
Currently, runtime.call16 is defined and used only on 32-bit
architectures, while 64-bit architectures all start at call32 and go
up from there. This led to unnecessary complexity because call16's
prototype needed to be in a different file, separate from all of the
other call* prototypes, which in turn led to it getting out of sync
with the other call* prototypes. This CL adds call16 on 64-bit
architectures, bringing them all into sync, and moves the call16
prototype to live with the others.
Prior to CL 31655 (in 2016), call16 couldn't be implemented on 64-bit
architectures because it needed at least four words of argument space
to invoke "callwritebarrier" after copying back the results. CL 31655
changed the way call* invoked the write barrier in preparation for the
hybrid barrier; since the hybrid barrier had to be invoked prior to
copying back results, it needed a different solution that didn't reuse
call*'s stack space. At this point, call16 was no longer a problem on
64-bit, but we never added it. Until now.
Change-Id: Id10ade0e4f75c6ea76afa6229ddaee2b994c27dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/259339
Trust: Austin Clements
Reviewed-by: Cherry Zhang
---
src/runtime/asm_amd64.s | 2 ++
src/runtime/asm_arm64.s | 2 ++
src/runtime/asm_mips64x.s | 1 +
src/runtime/asm_ppc64x.s | 2 ++
src/runtime/asm_riscv64.s | 1 +
src/runtime/asm_s390x.s | 2 ++
src/runtime/asm_wasm.s | 2 ++
src/runtime/stubs.go | 1 +
src/runtime/stubs32.go | 14 --------------
9 files changed, 13 insertions(+), 14 deletions(-)
delete mode 100644 src/runtime/stubs32.go
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index fa25c55b96..256f4112cd 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -470,6 +470,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
TEXT ·reflectcall(SB), NOSPLIT, $0-32
MOVLQZX argsize+24(FP), CX
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
@@ -537,6 +538,7 @@ TEXT callRet<>(SB), NOSPLIT, $32-0
CALL runtime·reflectcallmove(SB)
RET
+CALLFN(·call16, 16)
CALLFN(·call32, 32)
CALLFN(·call64, 64)
CALLFN(·call128, 128)
diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s
index 6b3d1e779e..5eda3063d7 100644
--- a/src/runtime/asm_arm64.s
+++ b/src/runtime/asm_arm64.s
@@ -331,6 +331,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
MOVWU argsize+24(FP), R16
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
@@ -416,6 +417,7 @@ TEXT callRet<>(SB), NOSPLIT, $40-0
// These have 8 added to make the overall frame size a multiple of 16,
// as required by the ABI. (There is another +8 for the saved LR.)
+CALLFN(·call16, 24 )
CALLFN(·call32, 40 )
CALLFN(·call64, 72 )
CALLFN(·call128, 136 )
diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s
index 7330f40e85..0ff1b24225 100644
--- a/src/runtime/asm_mips64x.s
+++ b/src/runtime/asm_mips64x.s
@@ -294,6 +294,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
MOVWU argsize+24(FP), R1
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s
index 23387a2165..603058a61b 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -372,6 +372,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
MOVWZ argsize+24(FP), R3
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
@@ -478,6 +479,7 @@ TEXT callRet<>(SB), NOSPLIT, $32-0
BL runtime·reflectcallmove(SB)
RET
+CALLFN(·call16, 16)
CALLFN(·call32, 32)
CALLFN(·call64, 64)
CALLFN(·call128, 128)
diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s
index 8f6c8773eb..4084ced7f8 100644
--- a/src/runtime/asm_riscv64.s
+++ b/src/runtime/asm_riscv64.s
@@ -342,6 +342,7 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
// func reflectcall(argtype *_type, fn, arg unsafe.Pointer, argsize uint32, retoffset uint32)
TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
MOVWU argsize+24(FP), T0
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s
index cb39451faa..46a434119b 100644
--- a/src/runtime/asm_s390x.s
+++ b/src/runtime/asm_s390x.s
@@ -383,6 +383,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
TEXT ·reflectcall(SB), NOSPLIT, $-8-32
MOVWZ argsize+24(FP), R3
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
@@ -461,6 +462,7 @@ TEXT callRet<>(SB), NOSPLIT, $32-0
BL runtime·reflectcallmove(SB)
RET
+CALLFN(·call16, 16)
CALLFN(·call32, 32)
CALLFN(·call64, 64)
CALLFN(·call128, 128)
diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s
index 7d88beb537..1275af136b 100644
--- a/src/runtime/asm_wasm.s
+++ b/src/runtime/asm_wasm.s
@@ -308,6 +308,7 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-32
MOVW argsize+24(FP), R0
+ DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128)
@@ -398,6 +399,7 @@ TEXT callRet<>(SB), NOSPLIT, $32-0
CALL runtime·reflectcallmove(SB)
RET
+CALLFN(·call16, 16)
CALLFN(·call32, 32)
CALLFN(·call64, 64)
CALLFN(·call128, 128)
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index b891a12fdd..bd2514e862 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -271,6 +271,7 @@ func return0()
// in asm_*.s
// not called directly; definitions here supply type information for traceback.
+func call16(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
func call32(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
func call64(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
func call128(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
diff --git a/src/runtime/stubs32.go b/src/runtime/stubs32.go
deleted file mode 100644
index c4715fe989..0000000000
--- a/src/runtime/stubs32.go
+++ /dev/null
@@ -1,14 +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 386 arm mips mipsle
-
-package runtime
-
-import "unsafe"
-
-// Declarations for runtime services implemented in C or assembly that
-// are only present on 32 bit systems.
-
-func call16(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
--
cgit v1.2.3-54-g00ecf
From a517c3422e808ae51533a0700e05d59e8a799136 Mon Sep 17 00:00:00 2001
From: Austin Clements
Date: Mon, 5 Oct 2020 12:17:30 -0400
Subject: runtime: clean up runtime.call* frame sizes on ARM64
ARM64 used to require that all assembly frame sizes were of the form
16*N+8 because ARM64 requires 16-byte SP alignment and the assembler
added an 8 byte LR slot. This made all of the runtime.call* frame
sizes wonky. The assembler now rounds up the frame size appropriately
after adding any additional slots it needs, so this is no longer
necessary.
This CL cleans up the frame sizes of these functions so they look the
way you'd expect and match all other architectures.
Change-Id: I47819092296b8983c43eadf2e66c7c1e0d518555
Reviewed-on: https://go-review.googlesource.com/c/go/+/259448
Trust: Austin Clements
Reviewed-by: Cherry Zhang
---
src/runtime/asm_arm64.s | 56 ++++++++++++++++++++++++-------------------------
1 file changed, 27 insertions(+), 29 deletions(-)
diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s
index 5eda3063d7..1f46d1962c 100644
--- a/src/runtime/asm_arm64.s
+++ b/src/runtime/asm_arm64.s
@@ -415,35 +415,33 @@ TEXT callRet<>(SB), NOSPLIT, $40-0
BL runtime·reflectcallmove(SB)
RET
-// These have 8 added to make the overall frame size a multiple of 16,
-// as required by the ABI. (There is another +8 for the saved LR.)
-CALLFN(·call16, 24 )
-CALLFN(·call32, 40 )
-CALLFN(·call64, 72 )
-CALLFN(·call128, 136 )
-CALLFN(·call256, 264 )
-CALLFN(·call512, 520 )
-CALLFN(·call1024, 1032 )
-CALLFN(·call2048, 2056 )
-CALLFN(·call4096, 4104 )
-CALLFN(·call8192, 8200 )
-CALLFN(·call16384, 16392 )
-CALLFN(·call32768, 32776 )
-CALLFN(·call65536, 65544 )
-CALLFN(·call131072, 131080 )
-CALLFN(·call262144, 262152 )
-CALLFN(·call524288, 524296 )
-CALLFN(·call1048576, 1048584 )
-CALLFN(·call2097152, 2097160 )
-CALLFN(·call4194304, 4194312 )
-CALLFN(·call8388608, 8388616 )
-CALLFN(·call16777216, 16777224 )
-CALLFN(·call33554432, 33554440 )
-CALLFN(·call67108864, 67108872 )
-CALLFN(·call134217728, 134217736 )
-CALLFN(·call268435456, 268435464 )
-CALLFN(·call536870912, 536870920 )
-CALLFN(·call1073741824, 1073741832 )
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
// func memhash32(p unsafe.Pointer, h uintptr) uintptr
TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24
--
cgit v1.2.3-54-g00ecf
From 9f24388a7d57a79d0d68c1c04cf3fa4f86338e21 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Sun, 4 Oct 2020 00:25:17 -0400
Subject: cmd/dist: test c-archive mode on ios/arm64
It is tested on darwin/arm64. Don't lose it when using GOOS=ios.
Updates #38485.
Change-Id: I7157d6b6f2850f2fd361e35ae310dd1ba9f31aa4
Reviewed-on: https://go-review.googlesource.com/c/go/+/259439
Trust: Cherry Zhang
Run-TryBot: Cherry Zhang
TryBot-Result: Go Bot
Reviewed-by: Ian Lance Taylor
---
src/cmd/dist/test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index f953a76963..da894e3eef 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -982,7 +982,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
}
switch pair {
case "aix-ppc64",
- "darwin-amd64", "darwin-arm64",
+ "darwin-amd64", "darwin-arm64", "ios-arm64",
"linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x",
"freebsd-amd64",
"windows-amd64", "windows-386":
--
cgit v1.2.3-54-g00ecf
From b064eb7e1bb1b138405b9c8da1d90c476a266ef5 Mon Sep 17 00:00:00 2001
From: Michael Matloob
Date: Thu, 24 Sep 2020 11:26:23 -0400
Subject: cmd/go: update go_windows_test to use test go binary
Most of the cmd/go tests build the cmd/go binary and run that binary to
test it, but TestAbsolutePath used the GOROOT's cmd/go instead, which
makes debugging confusing and means that make.bash has to be run in each
iteration cycle. Update TestAbsolutePath to use the same go binary as
the rest of the cmd/go tests.
Change-Id: Ib4e8ae707b66f1f75ceb346b98358f5604fd28c1
Reviewed-on: https://go-review.googlesource.com/c/go/+/256979
Trust: Michael Matloob
Run-TryBot: Michael Matloob
TryBot-Result: Go Bot
Reviewed-by: Jay Conrod
Reviewed-by: Bryan C. Mills
---
src/cmd/go/go_windows_test.go | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/cmd/go/go_windows_test.go b/src/cmd/go/go_windows_test.go
index 3999166ed9..02634f19f5 100644
--- a/src/cmd/go/go_windows_test.go
+++ b/src/cmd/go/go_windows_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package main_test
import (
- "internal/testenv"
"io/ioutil"
"os"
"os/exec"
@@ -17,7 +16,9 @@ import (
)
func TestAbsolutePath(t *testing.T) {
- t.Parallel()
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.parallel()
tmp, err := ioutil.TempDir("", "TestAbsolutePath")
if err != nil {
@@ -38,7 +39,7 @@ func TestAbsolutePath(t *testing.T) {
noVolume := file[len(filepath.VolumeName(file)):]
wrongPath := filepath.Join(dir, noVolume)
- cmd := exec.Command(testenv.GoToolPath(t), "build", noVolume)
+ cmd := exec.Command(tg.goTool(), "build", noVolume)
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err == nil {
--
cgit v1.2.3-54-g00ecf
From 72ee5bad9f9bd8979e14fab02fb07e39c5e9fd8c Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor
Date: Fri, 2 Oct 2020 16:03:37 -0700
Subject: cmd/cgo: split gofrontend mangling checks into cmd/internal/pkgpath
This is a step toward porting https://golang.org/cl/219817 from the
gofrontend repo to the main repo.
Note that this also corrects the implementation of the v2 mangling
scheme to use ..u and ..U where appropriate.
For #37272
Change-Id: I64a1e7ca1c84348efcbf1cf62049eeb05c830ed8
Reviewed-on: https://go-review.googlesource.com/c/go/+/259298
Trust: Ian Lance Taylor
Run-TryBot: Ian Lance Taylor
TryBot-Result: Go Bot
Reviewed-by: Than McIntosh
---
src/cmd/cgo/main.go | 3 +-
src/cmd/cgo/out.go | 118 ++++--------------------------
src/cmd/dist/buildtool.go | 1 +
src/cmd/internal/pkgpath/pkgpath.go | 114 +++++++++++++++++++++++++++++
src/cmd/internal/pkgpath/pkgpath_test.go | 121 +++++++++++++++++++++++++++++++
5 files changed, 252 insertions(+), 105 deletions(-)
create mode 100644 src/cmd/internal/pkgpath/pkgpath.go
create mode 100644 src/cmd/internal/pkgpath/pkgpath_test.go
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index ef3ed968e4..5c44fb72f4 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -224,8 +224,7 @@ var exportHeader = flag.String("exportheader", "", "where to write export header
var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
-var gccgoMangleCheckDone bool
-var gccgoNewmanglingInEffect bool
+var gccgoMangler func(string) string
var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
var goarch, goos string
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 03b8333b10..b447b07645 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -6,6 +6,7 @@ package main
import (
"bytes"
+ "cmd/internal/pkgpath"
"debug/elf"
"debug/macho"
"debug/pe"
@@ -15,7 +16,6 @@ import (
"go/token"
"internal/xcoff"
"io"
- "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -1282,112 +1282,24 @@ func (p *Package) writeExportHeader(fgcch io.Writer) {
fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
}
-// gccgoUsesNewMangling reports whether gccgo uses the new collision-free
-// packagepath mangling scheme (see determineGccgoManglingScheme for more
-// info).
-func gccgoUsesNewMangling() bool {
- if !gccgoMangleCheckDone {
- gccgoNewmanglingInEffect = determineGccgoManglingScheme()
- gccgoMangleCheckDone = true
- }
- return gccgoNewmanglingInEffect
-}
-
-const mangleCheckCode = `
-package läufer
-func Run(x int) int {
- return 1
-}
-`
-
-// determineGccgoManglingScheme performs a runtime test to see which
-// flavor of packagepath mangling gccgo is using. Older versions of
-// gccgo use a simple mangling scheme where there can be collisions
-// between packages whose paths are different but mangle to the same
-// string. More recent versions of gccgo use a new mangler that avoids
-// these collisions. Return value is whether gccgo uses the new mangling.
-func determineGccgoManglingScheme() bool {
-
- // Emit a small Go file for gccgo to compile.
- filepat := "*_gccgo_manglecheck.go"
- var f *os.File
- var err error
- if f, err = ioutil.TempFile(*objDir, filepat); err != nil {
- fatalf("%v", err)
- }
- gofilename := f.Name()
- defer os.Remove(gofilename)
-
- if err = ioutil.WriteFile(gofilename, []byte(mangleCheckCode), 0666); err != nil {
- fatalf("%v", err)
- }
-
- // Compile with gccgo, capturing generated assembly.
- gccgocmd := os.Getenv("GCCGO")
- if gccgocmd == "" {
- gpath, gerr := exec.LookPath("gccgo")
- if gerr != nil {
- fatalf("unable to locate gccgo: %v", gerr)
- }
- gccgocmd = gpath
- }
- cmd := exec.Command(gccgocmd, "-S", "-o", "-", gofilename)
- buf, cerr := cmd.CombinedOutput()
- if cerr != nil {
- fatalf("%s", cerr)
- }
-
- // New mangling: expect go.l..u00e4ufer.Run
- // Old mangling: expect go.l__ufer.Run
- return regexp.MustCompile(`go\.l\.\.u00e4ufer\.Run`).Match(buf)
-}
-
-// gccgoPkgpathToSymbolNew converts a package path to a gccgo-style
-// package symbol.
-func gccgoPkgpathToSymbolNew(ppath string) string {
- bsl := []byte{}
- changed := false
- for _, c := range []byte(ppath) {
- switch {
- case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z',
- '0' <= c && c <= '9', c == '_':
- bsl = append(bsl, c)
- case c == '.':
- bsl = append(bsl, ".x2e"...)
- default:
- changed = true
- encbytes := []byte(fmt.Sprintf("..z%02x", c))
- bsl = append(bsl, encbytes...)
- }
- }
- if !changed {
- return ppath
- }
- return string(bsl)
-}
-
-// gccgoPkgpathToSymbolOld converts a package path to a gccgo-style
-// package symbol using the older mangling scheme.
-func gccgoPkgpathToSymbolOld(ppath string) string {
- clean := func(r rune) rune {
- switch {
- case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
- '0' <= r && r <= '9':
- return r
- }
- return '_'
- }
- return strings.Map(clean, ppath)
-}
-
// gccgoPkgpathToSymbol converts a package path to a mangled packagepath
// symbol.
func gccgoPkgpathToSymbol(ppath string) string {
- if gccgoUsesNewMangling() {
- return gccgoPkgpathToSymbolNew(ppath)
- } else {
- return gccgoPkgpathToSymbolOld(ppath)
+ if gccgoMangler == nil {
+ var err error
+ cmd := os.Getenv("GCCGO")
+ if cmd == "" {
+ cmd, err = exec.LookPath("gccgo")
+ if err != nil {
+ fatalf("unable to locate gccgo: %v", err)
+ }
+ }
+ gccgoMangler, err = pkgpath.ToSymbolFunc(cmd, *objDir)
+ if err != nil {
+ fatalf("%v", err)
+ }
}
+ return gccgoMangler(ppath)
}
// Return the package prefix when using gccgo.
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index 79eab24d29..37b3d45977 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -67,6 +67,7 @@ var bootstrapDirs = []string{
"cmd/internal/obj/s390x",
"cmd/internal/obj/x86",
"cmd/internal/obj/wasm",
+ "cmd/internal/pkgpath",
"cmd/internal/src",
"cmd/internal/sys",
"cmd/link",
diff --git a/src/cmd/internal/pkgpath/pkgpath.go b/src/cmd/internal/pkgpath/pkgpath.go
new file mode 100644
index 0000000000..0b24468be6
--- /dev/null
+++ b/src/cmd/internal/pkgpath/pkgpath.go
@@ -0,0 +1,114 @@
+// 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 pkgpath determines the package path used by gccgo/GoLLVM symbols.
+// This package is not used for the gc compiler.
+package pkgpath
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+// ToSymbolFunc returns a function that may be used to convert a
+// package path into a string suitable for use as a symbol.
+// cmd is the gccgo/GoLLVM compiler in use, and tmpdir is a temporary
+// directory to pass to ioutil.TempFile.
+// For example, this returns a function that converts "net/http"
+// into a string like "net..z2fhttp". The actual string varies for
+// different gccgo/GoLLVM versions, which is why this returns a function
+// that does the conversion appropriate for the compiler in use.
+func ToSymbolFunc(cmd, tmpdir string) (func(string) string, error) {
+ // To determine the scheme used by cmd, we compile a small
+ // file and examine the assembly code. Older versions of gccgo
+ // use a simple mangling scheme where there can be collisions
+ // between packages whose paths are different but mangle to
+ // the same string. More recent versions use a new mangler
+ // that avoids these collisions.
+ const filepat = "*_gccgo_manglechck.go"
+ f, err := ioutil.TempFile(tmpdir, filepat)
+ if err != nil {
+ return nil, err
+ }
+ gofilename := f.Name()
+ f.Close()
+ defer os.Remove(gofilename)
+
+ if err := ioutil.WriteFile(gofilename, []byte(mangleCheckCode), 0644); err != nil {
+ return nil, err
+ }
+
+ command := exec.Command(cmd, "-S", "-o", "-", gofilename)
+ buf, err := command.Output()
+ if err != nil {
+ return nil, err
+ }
+
+ // New mangling: expect go.l..u00e4ufer.Run
+ // Old mangling: expect go.l__ufer.Run
+ if bytes.Contains(buf, []byte("go.l..u00e4ufer.Run")) {
+ return toSymbolV2, nil
+ } else if bytes.Contains(buf, []byte("go.l__ufer.Run")) {
+ return toSymbolV1, nil
+ } else {
+ return nil, errors.New(cmd + ": unrecognized mangling scheme")
+ }
+}
+
+// mangleCheckCode is the package we compile to determine the mangling scheme.
+const mangleCheckCode = `
+package läufer
+func Run(x int) int {
+ return 1
+}
+`
+
+// toSymbolV1 converts a package path using the original mangling scheme.
+func toSymbolV1(ppath string) string {
+ clean := func(r rune) rune {
+ switch {
+ case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
+ '0' <= r && r <= '9':
+ return r
+ }
+ return '_'
+ }
+ return strings.Map(clean, ppath)
+}
+
+// toSymbolV2 converts a package path using the newer mangling scheme.
+func toSymbolV2(ppath string) string {
+ // This has to build at boostrap time, so it has to build
+ // with Go 1.4, so we don't use strings.Builder.
+ bsl := make([]byte, 0, len(ppath))
+ changed := false
+ for _, c := range ppath {
+ if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_' {
+ bsl = append(bsl, byte(c))
+ continue
+ }
+ var enc string
+ switch {
+ case c == '.':
+ enc = ".x2e"
+ case c < 0x80:
+ enc = fmt.Sprintf("..z%02x", c)
+ case c < 0x10000:
+ enc = fmt.Sprintf("..u%04x", c)
+ default:
+ enc = fmt.Sprintf("..U%08x", c)
+ }
+ bsl = append(bsl, enc...)
+ changed = true
+ }
+ if !changed {
+ return ppath
+ }
+ return string(bsl)
+}
diff --git a/src/cmd/internal/pkgpath/pkgpath_test.go b/src/cmd/internal/pkgpath/pkgpath_test.go
new file mode 100644
index 0000000000..7355f81bae
--- /dev/null
+++ b/src/cmd/internal/pkgpath/pkgpath_test.go
@@ -0,0 +1,121 @@
+// 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 pkgpath
+
+import (
+ "os"
+ "testing"
+)
+
+const testEnvName = "GO_PKGPATH_TEST_COMPILER"
+
+// This init function supports TestToSymbolFunc. For simplicity,
+// we use the test binary itself as a sample gccgo driver.
+// We set an environment variable to specify how it should behave.
+func init() {
+ switch os.Getenv(testEnvName) {
+ case "":
+ return
+ case "v1":
+ os.Stdout.WriteString(`.string "go.l__ufer.Run"`)
+ os.Exit(0)
+ case "v2":
+ os.Stdout.WriteString(`.string "go.l..u00e4ufer.Run"`)
+ os.Exit(0)
+ case "error":
+ os.Stdout.WriteString(`unknown string`)
+ os.Exit(0)
+ }
+}
+
+func TestToSymbolFunc(t *testing.T) {
+ const input = "pä世🜃"
+ tests := []struct {
+ env string
+ fail bool
+ mangled string
+ }{
+ {
+ env: "v1",
+ mangled: "p___",
+ },
+ {
+ env: "v2",
+ mangled: "p..u00e4..u4e16..U0001f703",
+ },
+ {
+ env: "error",
+ fail: true,
+ },
+ }
+
+ cmd := os.Args[0]
+ tmpdir := t.TempDir()
+
+ defer os.Unsetenv(testEnvName)
+
+ for _, test := range tests {
+ t.Run(test.env, func(t *testing.T) {
+ os.Setenv(testEnvName, test.env)
+
+ fn, err := ToSymbolFunc(cmd, tmpdir)
+ if err != nil {
+ if !test.fail {
+ t.Errorf("ToSymbolFunc(%q, %q): unexpected error %v", cmd, tmpdir, err)
+ }
+ } else if test.fail {
+ t.Errorf("ToSymbolFunc(%q, %q) succeeded but expected to fail", cmd, tmpdir)
+ } else if got, want := fn(input), test.mangled; got != want {
+ t.Errorf("ToSymbolFunc(%q, %q)(%q) = %q, want %q", cmd, tmpdir, input, got, want)
+ }
+ })
+ }
+}
+
+var symbolTests = []struct {
+ input, v1, v2 string
+}{
+ {
+ "",
+ "",
+ "",
+ },
+ {
+ "bytes",
+ "bytes",
+ "bytes",
+ },
+ {
+ "net/http",
+ "net_http",
+ "net..z2fhttp",
+ },
+ {
+ "golang.org/x/net/http",
+ "golang_org_x_net_http",
+ "golang.x2eorg..z2fx..z2fnet..z2fhttp",
+ },
+ {
+ "pä世.🜃",
+ "p____",
+ "p..u00e4..u4e16.x2e..U0001f703",
+ },
+}
+
+func TestV1(t *testing.T) {
+ for _, test := range symbolTests {
+ if got, want := toSymbolV1(test.input), test.v1; got != want {
+ t.Errorf("toSymbolV1(%q) = %q, want %q", test.input, got, want)
+ }
+ }
+}
+
+func TestV2(t *testing.T) {
+ for _, test := range symbolTests {
+ if got, want := toSymbolV2(test.input), test.v2; got != want {
+ t.Errorf("toSymbolV2(%q) = %q, want %q", test.input, got, want)
+ }
+ }
+}
--
cgit v1.2.3-54-g00ecf
From a65bc048bf388e399af9bcfd726cd0f11bba7c8e Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor
Date: Fri, 2 Oct 2020 16:17:30 -0700
Subject: cmd/go: use cmd/internal/pkgpath for gccgo pkgpath symbol
Fixes #37272
Change-Id: I6554fd5e5400acb20c5a7e96b1d6cb1a1afb9871
Reviewed-on: https://go-review.googlesource.com/c/go/+/259299
Trust: Ian Lance Taylor
Run-TryBot: Ian Lance Taylor
TryBot-Result: Go Bot
Reviewed-by: Than McIntosh
---
src/cmd/go/internal/work/gccgo.go | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
index 4c1f36dbd6..dd5adf2d7b 100644
--- a/src/cmd/go/internal/work/gccgo.go
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -11,11 +11,13 @@ import (
"os/exec"
"path/filepath"
"strings"
+ "sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
+ "cmd/internal/pkgpath"
)
// The Gccgo toolchain.
@@ -174,7 +176,7 @@ func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]strin
ofiles = append(ofiles, ofile)
sfile = mkAbs(p.Dir, sfile)
defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
- if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
+ if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
}
defs = tools.maybePIC(defs)
@@ -531,7 +533,7 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error
cfile = mkAbs(p.Dir, cfile)
defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
defs = append(defs, b.gccArchArgs()...)
- if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
+ if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
}
compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
@@ -568,14 +570,19 @@ func gccgoPkgpath(p *load.Package) string {
return p.ImportPath
}
-func gccgoCleanPkgpath(p *load.Package) string {
- clean := func(r rune) rune {
- switch {
- case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
- '0' <= r && r <= '9':
- return r
+var gccgoToSymbolFuncOnce sync.Once
+var gccgoToSymbolFunc func(string) string
+
+func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string {
+ gccgoToSymbolFuncOnce.Do(func() {
+ fn, err := pkgpath.ToSymbolFunc(tools.compiler(), b.WorkDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
+ base.SetExitStatus(2)
+ base.Exit()
}
- return '_'
- }
- return strings.Map(clean, gccgoPkgpath(p))
+ gccgoToSymbolFunc = fn
+ })
+
+ return gccgoToSymbolFunc(gccgoPkgpath(p))
}
--
cgit v1.2.3-54-g00ecf
From a9c75ecd3da2d87ce08b2e75bd4f332185cd7fc8 Mon Sep 17 00:00:00 2001
From: Keith Randall
Date: Fri, 2 Oct 2020 15:48:50 -0700
Subject: cmd/compile: export notinheap annotation to object file
In the rare case when a cgo type makes it into an object file, we need
the go:notinheap annotation to go with it.
Fixes #41761
Change-Id: I541500cb1a03de954881aef659f96fc0b7738848
Reviewed-on: https://go-review.googlesource.com/c/go/+/259297
Trust: Keith Randall
Run-TryBot: Keith Randall
TryBot-Result: Go Bot
Reviewed-by: Cherry Zhang
---
misc/cgo/test/testdata/issue41761.go | 20 ++++++++++++++++++++
misc/cgo/test/testdata/issue41761a/a.go | 14 ++++++++++++++
src/cmd/compile/internal/gc/iexport.go | 2 ++
src/cmd/compile/internal/gc/iimport.go | 2 +-
src/cmd/compile/internal/gc/lex.go | 2 +-
5 files changed, 38 insertions(+), 2 deletions(-)
create mode 100644 misc/cgo/test/testdata/issue41761.go
create mode 100644 misc/cgo/test/testdata/issue41761a/a.go
diff --git a/misc/cgo/test/testdata/issue41761.go b/misc/cgo/test/testdata/issue41761.go
new file mode 100644
index 0000000000..919c749251
--- /dev/null
+++ b/misc/cgo/test/testdata/issue41761.go
@@ -0,0 +1,20 @@
+// 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 cgotest
+
+/*
+ typedef struct S S;
+*/
+import "C"
+
+import (
+ "cgotest/issue41761a"
+ "testing"
+)
+
+func test41761(t *testing.T) {
+ var x issue41761a.T
+ _ = (*C.struct_S)(x.X)
+}
diff --git a/misc/cgo/test/testdata/issue41761a/a.go b/misc/cgo/test/testdata/issue41761a/a.go
new file mode 100644
index 0000000000..ca5c18191e
--- /dev/null
+++ b/misc/cgo/test/testdata/issue41761a/a.go
@@ -0,0 +1,14 @@
+// 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 issue41761a
+
+/*
+ typedef struct S S;
+*/
+import "C"
+
+type T struct {
+ X *C.S
+}
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index b3f50b63af..3be3b0a213 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -1017,6 +1017,8 @@ func (w *exportWriter) symIdx(s *types.Sym) {
}
func (w *exportWriter) typeExt(t *types.Type) {
+ // Export whether this type is marked notinheap.
+ w.bool(t.NotInHeap())
// For type T, export the index of type descriptor symbols of T and *T.
if i, ok := typeSymIdx[t]; ok {
w.int64(i[0])
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index 4169222c14..0c5e469c57 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -596,7 +596,6 @@ func (r *importReader) typ1() *types.Type {
// Ensure we expand the interface in the frontend (#25055).
checkwidth(t)
-
return t
}
}
@@ -711,6 +710,7 @@ func (r *importReader) symIdx(s *types.Sym) {
}
func (r *importReader) typeExt(t *types.Type) {
+ t.SetNotInHeap(r.bool())
i, pi := r.int64(), r.int64()
if i != -1 && pi != -1 {
typeSymIdx[t] = [2]int64{i, pi}
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 1a344c6566..25bc0399ce 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -48,7 +48,7 @@ const (
Nowritebarrierrec // error on write barrier in this or recursive callees
Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
- // Runtime-only type pragmas
+ // Runtime and cgo type pragmas
NotInHeap // values of this type must not be heap allocated
)
--
cgit v1.2.3-54-g00ecf
From 56284f9d29d17869cd70847693c51319408710b3 Mon Sep 17 00:00:00 2001
From: Dmitri Shuralyov
Date: Fri, 25 Sep 2020 17:10:24 -0400
Subject: src/buildall.bash: remove mobile filter
Mobile targets are not supported by misc-compile trybots, as tracked in
golang.org/issue/25963, and need to be filtered out. The buildall.bash
script was created in CL 9438, back when it was a single all-compile
builder, and it was easier to filter out mobile targets in the script
than to come up with a pattern that matches all non-mobile targets.
As of CL 254740, all mobile targets (Android and iOS) have unique GOOS
values. That makes it it easy to filter them out in x/build/dashboard.
This was done in CL 258057. As a result, it's now viable to simplify
this script and perform all misc-compile target selection in x/build,
rather than having it spread it across two places.
Also, as of CL 10750, the all-compile builder has turned into multiple
misc-compile builders, so update the script description accordingly.
Updates #41610.
Change-Id: I1e33260ac18cf0a70bb68cd8e3db5587100c7e87
Reviewed-on: https://go-review.googlesource.com/c/go/+/257962
Run-TryBot: Dmitri Shuralyov
TryBot-Result: Go Bot
Reviewed-by: Alexander Rakoczy
Trust: Dmitri Shuralyov
---
src/buildall.bash | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/buildall.bash b/src/buildall.bash
index dc67c0630f..19ea172c5b 100755
--- a/src/buildall.bash
+++ b/src/buildall.bash
@@ -3,10 +3,10 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-# Usage: buildall.sh [-e] [pattern]
+# Usage: buildall.bash [-e] [pattern]
#
# buildall.bash builds the standard library for all Go-supported
-# architectures. It is used by the "all-compile" trybot builder,
+# architectures. It is used by the "misc-compile" trybot builders,
# as a smoke test to quickly flag portability issues.
#
# Options:
@@ -42,7 +42,7 @@ gettargets() {
}
selectedtargets() {
- gettargets | egrep -v 'android-arm|darwin-arm64' | egrep "$pattern"
+ gettargets | egrep "$pattern"
}
# put linux first in the target list to get all the architectures up front.
--
cgit v1.2.3-54-g00ecf
From e7a7a403f92aef0eda8bf9f00091c8b21e2223a3 Mon Sep 17 00:00:00 2001
From: Dmitri Shuralyov
Date: Mon, 28 Sep 2020 14:17:37 -0400
Subject: src/buildall.bash: remove linux-386-387 target
Support for GO386=387 is being dropped in Go 1.16. There
is no need for the target to be available for testing on
the master branch (where Go 1.16 development is ongoing).
For #40255.
Change-Id: I4a4ee80b0c0a535b6b0b246fe991f26964eb07ca
Reviewed-on: https://go-review.googlesource.com/c/go/+/257963
Reviewed-by: Ian Lance Taylor
Trust: Dmitri Shuralyov
---
src/buildall.bash | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/buildall.bash b/src/buildall.bash
index 19ea172c5b..7b3751f42e 100755
--- a/src/buildall.bash
+++ b/src/buildall.bash
@@ -37,7 +37,6 @@ GOROOT="$(cd .. && pwd)"
gettargets() {
../bin/go tool dist list | sed -e 's|/|-|'
- echo linux-386-387
echo linux-arm-arm5
}
@@ -64,15 +63,11 @@ do
echo "### Building $target"
export GOOS=$(echo $target | sed 's/-.*//')
export GOARCH=$(echo $target | sed 's/.*-//')
- unset GO386 GOARM
+ unset GOARM
if [ "$GOARCH" = "arm5" ]; then
export GOARCH=arm
export GOARM=5
fi
- if [ "$GOARCH" = "387" ]; then
- export GOARCH=386
- export GO386=387
- fi
# Build and vet everything.
# cmd/go/internal/work/exec.go enables the same vet flags during go test of std cmd
--
cgit v1.2.3-54-g00ecf
From 5d12434eee031e3db9e0bfe753c663b565b6a0f9 Mon Sep 17 00:00:00 2001
From: Alexey Vilenskiy
Date: Fri, 14 Aug 2020 11:37:31 +0300
Subject: reflect: support multiple keys in struct tags
Fixes #40281
Change-Id: Ie624bce3a78a06d7ed71bba1f501e66802dffd13
Reviewed-on: https://go-review.googlesource.com/c/go/+/248341
Reviewed-by: Ian Lance Taylor
Trust: Dmitri Shuralyov
---
src/reflect/all_test.go | 170 ++++++++++++++++++++++++++++++++++++++++++++++++
src/reflect/type.go | 27 +++++++-
2 files changed, 194 insertions(+), 3 deletions(-)
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 0684eab973..a12712d254 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -7165,6 +7165,176 @@ func TestMapIterDelete1(t *testing.T) {
}
}
+func TestStructTagLookup(t *testing.T) {
+ var tests = []struct {
+ tag StructTag
+ key string
+ expectedValue string
+ expectedOK bool
+ }{
+ {
+ tag: `json:"json_value_1"`,
+ key: "json",
+ expectedValue: "json_value_1",
+ expectedOK: true,
+ },
+ {
+ tag: `json:"json_value_2" xml:"xml_value_2"`,
+ key: "json",
+ expectedValue: "json_value_2",
+ expectedOK: true,
+ },
+ {
+ tag: `json:"json_value_3" xml:"xml_value_3"`,
+ key: "xml",
+ expectedValue: "xml_value_3",
+ expectedOK: true,
+ },
+ {
+ tag: `bson json:"shared_value_4"`,
+ key: "json",
+ expectedValue: "shared_value_4",
+ expectedOK: true,
+ },
+ {
+ tag: `bson json:"shared_value_5"`,
+ key: "bson",
+ expectedValue: "shared_value_5",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_1,omitempty" other:"value_1"`,
+ key: "xml",
+ expectedValue: "field_1,omitempty",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_2,omitempty" other:"value_2"`,
+ key: "form",
+ expectedValue: "field_2,omitempty",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_3,omitempty" other:"value_3"`,
+ key: "other",
+ expectedValue: "value_3",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_4" other:"value_4"`,
+ key: "json",
+ expectedValue: "field_4",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_5" other:"value_5"`,
+ key: "non_existing",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json "json_6"`,
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json:"json_7" bson "bson_7"`,
+ key: "json",
+ expectedValue: "json_7",
+ expectedOK: true,
+ },
+ {
+ tag: `json:"json_8" xml "xml_8"`,
+ key: "xml",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json bson xml form "form_9" other:"value_9"`,
+ key: "bson",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json bson xml form "form_10" other:"value_10"`,
+ key: "other",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json bson xml form:"form_11" other "value_11"`,
+ key: "json",
+ expectedValue: "form_11",
+ expectedOK: true,
+ },
+ {
+ tag: `tag1`,
+ key: "tag1",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `tag2 :"hello_2"`,
+ key: "tag2",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `tag3: "hello_3"`,
+ key: "tag3",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json\x7fbson: \"hello_4\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json\x7fbson: \"hello_5\"",
+ key: "bson",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json bson:\x7f\"hello_6\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json bson:\x7f\"hello_7\"",
+ key: "bson",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json\x09bson:\"hello_8\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "a\x7fb json:\"val\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ }
+
+ for _, test := range tests {
+ v, ok := test.tag.Lookup(test.key)
+ if v != test.expectedValue {
+ t.Errorf("struct tag lookup failed, got %s, want %s", v, test.expectedValue)
+ }
+ if ok != test.expectedOK {
+ t.Errorf("struct tag lookup failed, got %t, want %t", ok, test.expectedOK)
+ }
+ }
+}
+
// iterateToString returns the set of elements
// returned by an iterator in readable form.
func iterateToString(it *MapIter) string {
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 44c96fea82..a3a616701b 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1130,6 +1130,9 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
// When modifying this code, also update the validateStructTag code
// in cmd/vet/structtag.go.
+ // keyFound indicates that such key on the left side has already been found.
+ var keyFound bool
+
for tag != "" {
// Skip leading space.
i := 0
@@ -1149,11 +1152,29 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
i++
}
- if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+ if i == 0 || i+1 >= len(tag) || tag[i] < ' ' || tag[i] == 0x7f {
break
}
name := string(tag[:i])
- tag = tag[i+1:]
+ tag = tag[i:]
+
+ // If we found a space char here - assume that we have a tag with
+ // multiple keys.
+ if tag[0] == ' ' {
+ if name == key {
+ keyFound = true
+ }
+ continue
+ }
+
+ // Spaces were filtered above so we assume that here we have
+ // only valid tag value started with `:"`.
+ if tag[0] != ':' || tag[1] != '"' {
+ break
+ }
+
+ // Remove the colon leaving tag at the start of the quoted string.
+ tag = tag[1:]
// Scan quoted string to find value.
i = 1
@@ -1169,7 +1190,7 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
qvalue := string(tag[:i+1])
tag = tag[i+1:]
- if key == name {
+ if key == name || keyFound {
value, err := strconv.Unquote(qvalue)
if err != nil {
break
--
cgit v1.2.3-54-g00ecf
From 694025e74f861bf48a737a8b42612d6397f1879b Mon Sep 17 00:00:00 2001
From: David Chase
Date: Mon, 5 Oct 2020 12:07:00 -0400
Subject: cmd/compile: avoid applying ARM CMP->CMN rewrite in unsigned context
Fixes #41780.
Change-Id: I1dc7c19a9f057650905da3a96214c2ff4abb51be
Reviewed-on: https://go-review.googlesource.com/c/go/+/259450
Trust: David Chase
Run-TryBot: David Chase
TryBot-Result: Go Bot
Reviewed-by: Cherry Zhang
---
src/cmd/compile/internal/ssa/gen/ARM.rules | 4 +-
src/cmd/compile/internal/ssa/rewriteARM.go | 243 +++++++++++++++++++++++++----
test/fixedbugs/issue41780.go | 39 +++++
3 files changed, 257 insertions(+), 29 deletions(-)
create mode 100644 test/fixedbugs/issue41780.go
diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules
index 9490805f46..aad7236d59 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM.rules
@@ -1263,8 +1263,8 @@
(SRLconst (SLLconst x [c]) [d]) && objabi.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x)
// comparison simplification
-(CMP x (RSBconst [0] y)) => (CMN x y)
-(CMN x (RSBconst [0] y)) => (CMP x y)
+((LT|LE|EQ|NE|GE|GT) (CMP x (RSBconst [0] y))) => ((LT|LE|EQ|NE|GE|GT) (CMN x y)) // sense of carry bit not preserved
+((LT|LE|EQ|NE|GE|GT) (CMN x (RSBconst [0] y))) => ((LT|LE|EQ|NE|GE|GT) (CMP x y)) // sense of carry bit not preserved
(EQ (CMPconst [0] l:(SUB x y)) yes no) && l.Uses==1 => (EQ (CMP x y) yes no)
(EQ (CMPconst [0] l:(MULS x y a)) yes no) && l.Uses==1 => (EQ (CMP a (MUL x y)) yes no)
(EQ (CMPconst [0] l:(SUBconst [c] x)) yes no) && l.Uses==1 => (EQ (CMPconst [c] x) yes no)
diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go
index 4e44165169..435da688b7 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -3362,21 +3362,6 @@ func rewriteValueARM_OpARMCMN(v *Value) bool {
}
break
}
- // match: (CMN x (RSBconst [0] y))
- // result: (CMP x y)
- for {
- for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
- x := v_0
- if v_1.Op != OpARMRSBconst || auxIntToInt32(v_1.AuxInt) != 0 {
- continue
- }
- y := v_1.Args[0]
- v.reset(OpARMCMP)
- v.AddArg2(x, y)
- return true
- }
- break
- }
return false
}
func rewriteValueARM_OpARMCMNconst(v *Value) bool {
@@ -3938,18 +3923,6 @@ func rewriteValueARM_OpARMCMP(v *Value) bool {
v.AddArg(v0)
return true
}
- // match: (CMP x (RSBconst [0] y))
- // result: (CMN x y)
- for {
- x := v_0
- if v_1.Op != OpARMRSBconst || auxIntToInt32(v_1.AuxInt) != 0 {
- break
- }
- y := v_1.Args[0]
- v.reset(OpARMCMN)
- v.AddArg2(x, y)
- return true
- }
return false
}
func rewriteValueARM_OpARMCMPD(v *Value) bool {
@@ -16002,6 +15975,42 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMEQ, cmp)
return true
}
+ // match: (EQ (CMP x (RSBconst [0] y)))
+ // result: (EQ (CMN x y))
+ for b.Controls[0].Op == OpARMCMP {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ break
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMEQ, v0)
+ return true
+ }
+ // match: (EQ (CMN x (RSBconst [0] y)))
+ // result: (EQ (CMP x y))
+ for b.Controls[0].Op == OpARMCMN {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ x := v_0_0
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMEQ, v0)
+ return true
+ }
+ break
+ }
// match: (EQ (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (EQ (CMP x y) yes no)
@@ -16848,6 +16857,42 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMLE, cmp)
return true
}
+ // match: (GE (CMP x (RSBconst [0] y)))
+ // result: (GE (CMN x y))
+ for b.Controls[0].Op == OpARMCMP {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ break
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMGE, v0)
+ return true
+ }
+ // match: (GE (CMN x (RSBconst [0] y)))
+ // result: (GE (CMP x y))
+ for b.Controls[0].Op == OpARMCMN {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ x := v_0_0
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMGE, v0)
+ return true
+ }
+ break
+ }
// match: (GE (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (GEnoov (CMP x y) yes no)
@@ -17728,6 +17773,42 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMLT, cmp)
return true
}
+ // match: (GT (CMP x (RSBconst [0] y)))
+ // result: (GT (CMN x y))
+ for b.Controls[0].Op == OpARMCMP {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ break
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMGT, v0)
+ return true
+ }
+ // match: (GT (CMN x (RSBconst [0] y)))
+ // result: (GT (CMP x y))
+ for b.Controls[0].Op == OpARMCMN {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ x := v_0_0
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMGT, v0)
+ return true
+ }
+ break
+ }
// match: (GT (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (GTnoov (CMP x y) yes no)
@@ -18699,6 +18780,42 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMGE, cmp)
return true
}
+ // match: (LE (CMP x (RSBconst [0] y)))
+ // result: (LE (CMN x y))
+ for b.Controls[0].Op == OpARMCMP {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ break
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMLE, v0)
+ return true
+ }
+ // match: (LE (CMN x (RSBconst [0] y)))
+ // result: (LE (CMP x y))
+ for b.Controls[0].Op == OpARMCMN {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ x := v_0_0
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMLE, v0)
+ return true
+ }
+ break
+ }
// match: (LE (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (LEnoov (CMP x y) yes no)
@@ -19579,6 +19696,42 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMGT, cmp)
return true
}
+ // match: (LT (CMP x (RSBconst [0] y)))
+ // result: (LT (CMN x y))
+ for b.Controls[0].Op == OpARMCMP {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ break
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMLT, v0)
+ return true
+ }
+ // match: (LT (CMN x (RSBconst [0] y)))
+ // result: (LT (CMP x y))
+ for b.Controls[0].Op == OpARMCMN {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ x := v_0_0
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMLT, v0)
+ return true
+ }
+ break
+ }
// match: (LT (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (LTnoov (CMP x y) yes no)
@@ -20609,6 +20762,42 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMNE, cmp)
return true
}
+ // match: (NE (CMP x (RSBconst [0] y)))
+ // result: (NE (CMN x y))
+ for b.Controls[0].Op == OpARMCMP {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ break
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMNE, v0)
+ return true
+ }
+ // match: (NE (CMN x (RSBconst [0] y)))
+ // result: (NE (CMP x y))
+ for b.Controls[0].Op == OpARMCMN {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ x := v_0_0
+ if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockARMNE, v0)
+ return true
+ }
+ break
+ }
// match: (NE (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (NE (CMP x y) yes no)
diff --git a/test/fixedbugs/issue41780.go b/test/fixedbugs/issue41780.go
new file mode 100644
index 0000000000..632c144a48
--- /dev/null
+++ b/test/fixedbugs/issue41780.go
@@ -0,0 +1,39 @@
+// run
+
+// 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.
+
+// Checks that conversion of CMP(x,-y) -> CMN(x,y) is only applied in correct context.
+
+package main
+
+type decimal struct {
+ d [8]byte // digits, big-endian representation
+ dp int // decimal point
+}
+
+var powtab = []int{1, 3, 6, 9, 13, 16, 19, 23, 26}
+
+//go:noinline
+func foo(d *decimal) int {
+ exp := int(d.d[1])
+ if d.dp < 0 || d.dp == 0 && d.d[0] < '5' {
+ var n int
+ if -d.dp >= len(powtab) {
+ n = 27
+ } else {
+ n = powtab[-d.dp] // incorrect CMP -> CMN substitution causes indexing panic.
+ }
+ exp += n
+ }
+ return exp
+}
+
+func main() {
+ var d decimal
+ d.d[0] = '1'
+ if foo(&d) != 1 {
+ println("FAILURE (though not the one this test was written to catch)")
+ }
+}
--
cgit v1.2.3-54-g00ecf
From 8e203884dcd5c525208ffb137fed76fd2d09ffc4 Mon Sep 17 00:00:00 2001
From: Alberto Donizetti
Date: Thu, 1 Oct 2020 14:00:48 +0200
Subject: doc: fix typo in contribute.html
Change-Id: Ica27c4a9e4c364d94250aebfc4c2b59cff7f4a8f
Reviewed-on: https://go-review.googlesource.com/c/go/+/258679
Trust: Alberto Donizetti
Reviewed-by: Dmitri Shuralyov
---
doc/contribute.html | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/doc/contribute.html b/doc/contribute.html
index 3fb617b863..09d43313ff 100644
--- a/doc/contribute.html
+++ b/doc/contribute.html
@@ -806,10 +806,9 @@ tracker will automatically mark the issue as fixed.
If the change is a partial step towards the resolution of the issue,
-uses the notation "Updates #12345".
-This will leave a comment in the issue
-linking back to the change in Gerrit, but it will not close the issue
-when the change is applied.
+write "Updates #12345" instead.
+This will leave a comment in the issue linking back to the change in
+Gerrit, but it will not close the issue when the change is applied.
--
cgit v1.2.3-54-g00ecf
From d2a80f3fb5b44450e0b304ac5a718f99c053d82a Mon Sep 17 00:00:00 2001
From: Luca Spiller
Date: Tue, 6 Oct 2020 08:12:45 +0000
Subject: crypto/tls: fix typo in spelling of permanentError
Change-Id: I819c121ff388460ec348af773ef94b44416a2ea9
GitHub-Last-Rev: 98dd8fb25cecb73e88d107e0a35e3e63a53dfd09
GitHub-Pull-Request: golang/go#41785
Reviewed-on: https://go-review.googlesource.com/c/go/+/259517
Run-TryBot: Emmanuel Odeke
TryBot-Result: Go Bot
Reviewed-by: Emmanuel Odeke
Reviewed-by: Filippo Valsorda
Trust: Emmanuel Odeke
---
src/crypto/tls/conn.go | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
index 5dff76c988..f1d4cb926c 100644
--- a/src/crypto/tls/conn.go
+++ b/src/crypto/tls/conn.go
@@ -168,18 +168,18 @@ type halfConn struct {
trafficSecret []byte // current TLS 1.3 traffic secret
}
-type permamentError struct {
+type permanentError struct {
err net.Error
}
-func (e *permamentError) Error() string { return e.err.Error() }
-func (e *permamentError) Unwrap() error { return e.err }
-func (e *permamentError) Timeout() bool { return e.err.Timeout() }
-func (e *permamentError) Temporary() bool { return false }
+func (e *permanentError) Error() string { return e.err.Error() }
+func (e *permanentError) Unwrap() error { return e.err }
+func (e *permanentError) Timeout() bool { return e.err.Timeout() }
+func (e *permanentError) Temporary() bool { return false }
func (hc *halfConn) setErrorLocked(err error) error {
if e, ok := err.(net.Error); ok {
- hc.err = &permamentError{err: e}
+ hc.err = &permanentError{err: e}
} else {
hc.err = err
}
--
cgit v1.2.3-54-g00ecf
From f8d80977b784fd4879963e61dc9fca1fc9bf2193 Mon Sep 17 00:00:00 2001
From: David Chase
Date: Fri, 2 Oct 2020 14:53:48 -0400
Subject: cmd/compile: correct leaf type when "selecting" singleton
register-sized struct
Two part fix:
1) bring the type "correction" forward from a later CL in the expand calls series
2) when a leaf-selwect is rewritten in place, update the type (it might have been
changed by the type correction in 1).
Fixes #41736.
Change-Id: Id097efd10481bf0ad92aaead81a7207221c144b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/259203
Trust: David Chase
Run-TryBot: David Chase
TryBot-Result: Go Bot
Reviewed-by: Cherry Zhang
---
src/cmd/compile/internal/ssa/config.go | 2 +-
src/cmd/compile/internal/ssa/expand_calls.go | 41 +++++++++--
test/fixedbugs/issue41736.go | 105 +++++++++++++++++++++++++++
3 files changed, 141 insertions(+), 7 deletions(-)
create mode 100644 test/fixedbugs/issue41736.go
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 649b5ba820..f1a748309c 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -195,7 +195,7 @@ const (
ClassParamOut // return value
)
-const go116lateCallExpansion = false
+const go116lateCallExpansion = true
// LateCallExpansionEnabledWithin returns true if late call expansion should be tested
// within compilation of a function/method triggered by GOSSAHASH (defaults to "yes").
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 7b1d656b64..992936b2d3 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -58,6 +58,29 @@ func expandCalls(f *Func) {
return t.IsStruct() || t.IsArray() || regSize == 4 && t.Size() > 4 && t.IsInteger()
}
+ // removeTrivialWrapperTypes unwraps layers of
+ // struct { singleField SomeType } and [1]SomeType
+ // until a non-wrapper type is reached. This is useful
+ // for working with assignments to/from interface data
+ // fields (either second operand to OpIMake or OpIData)
+ // where the wrapping or type conversion can be elided
+ // because of type conversions/assertions in source code
+ // that do not appear in SSA.
+ removeTrivialWrapperTypes := func(t *types.Type) *types.Type {
+ for {
+ if t.IsStruct() && t.NumFields() == 1 {
+ t = t.Field(0).Type
+ continue
+ }
+ if t.IsArray() && t.NumElem() == 1 {
+ t = t.Elem()
+ continue
+ }
+ break
+ }
+ return t
+ }
+
// Calls that need lowering have some number of inputs, including a memory input,
// and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
@@ -84,7 +107,7 @@ func expandCalls(f *Func) {
// rewrite v as a Copy of call -- the replacement call will produce a mem.
leaf.copyOf(call)
} else {
- leafType := leaf.Type
+ leafType := removeTrivialWrapperTypes(leaf.Type)
pt := types.NewPtr(leafType)
if canSSAType(leafType) {
off := f.ConstOffPtrSP(pt, offset+aux.OffsetOfResult(which), sp)
@@ -92,6 +115,7 @@ func expandCalls(f *Func) {
if leaf.Block == call.Block {
leaf.reset(OpLoad)
leaf.SetArgs2(off, call)
+ leaf.Type = leafType
} else {
w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
leaf.copyOf(w)
@@ -192,6 +216,13 @@ func expandCalls(f *Func) {
case types.TARRAY:
elt := t.Elem()
+ if src.Op == OpIData && t.NumElem() == 1 && t.Width == regSize && elt.Width == regSize {
+ t = removeTrivialWrapperTypes(t)
+ if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY {
+ f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it")
+ }
+ break // handle the leaf type.
+ }
for i := int64(0); i < t.NumElem(); i++ {
sel := src.Block.NewValue1I(pos, OpArraySelect, elt, i, src)
mem = splitStore(dst, sel, mem, v, elt, offset+i*elt.Width, firstStorePos)
@@ -199,7 +230,7 @@ func expandCalls(f *Func) {
}
return mem
case types.TSTRUCT:
- if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
+ if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
// This peculiar test deals with accesses to immediate interface data.
// It works okay because everything is the same size.
// Example code that triggers this can be found in go/constant/value.go, function ToComplex
@@ -207,11 +238,9 @@ func expandCalls(f *Func) {
// v121 (+882) = StaticLECall {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1
// This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)"
// Guard against "struct{struct{*foo}}"
- for t.Etype == types.TSTRUCT && t.NumFields() == 1 {
- t = t.Field(0).Type
- }
+ t = removeTrivialWrapperTypes(t)
if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY {
- f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct in it")
+ f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it")
}
break // handle the leaf type.
}
diff --git a/test/fixedbugs/issue41736.go b/test/fixedbugs/issue41736.go
new file mode 100644
index 0000000000..36f127f4fb
--- /dev/null
+++ b/test/fixedbugs/issue41736.go
@@ -0,0 +1,105 @@
+// compile
+
+// 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 p
+
+type I struct {
+ x int64
+}
+
+type F struct {
+ x float64
+}
+
+type C struct {
+ x *complex128
+}
+
+type D struct {
+ x complex64
+}
+
+type A [1]*complex128
+
+//go:noinline
+func (i I) X() C {
+ cx := complex(0, float64(i.x))
+ return C{&cx}
+}
+
+//go:noinline
+func (f F) X() C {
+ cx := complex(f.x, 0)
+ return C{&cx}
+}
+
+//go:noinline
+func (c C) X() C {
+ cx := complex(imag(*c.x), real(*c.x))
+ return C{&cx}
+}
+
+//go:noinline
+func (d D) X() C {
+ cx := complex(float64(imag(d.x)), -float64(real(d.x)))
+ return C{&cx}
+}
+
+//go:noinline
+func (a A) X() C {
+ cx := complex(-float64(imag(*a[0])), float64(real(*a[0])))
+ return C{&cx}
+}
+
+//go:noinline
+func (i I) id() I {
+ return i
+}
+
+//go:noinline
+func (f F) id() F {
+ return f
+}
+
+//go:noinline
+func (c C) id() C {
+ return c
+}
+
+//go:noinline
+func (d D) id() D {
+ return d
+}
+
+//go:noinline
+func (a A) id() A {
+ return a
+}
+
+type T interface {
+ X() C
+}
+
+func G(x []T) []T {
+ var y []T
+ for _, a := range x {
+ var v T
+ switch u := a.(type) {
+ case I:
+ v = u.id()
+ case F:
+ v = u.id()
+ case C:
+ v = u.id()
+ case D:
+ v = u.id()
+ case A:
+ v = u.id()
+ }
+ y = append(y, v)
+ }
+ return y
+}
--
cgit v1.2.3-54-g00ecf
From ab2a5b48665eed6d670d719cdef5335bc3602359 Mon Sep 17 00:00:00 2001
From: Michael Matloob
Date: Tue, 11 Aug 2020 12:57:01 -0400
Subject: cmd/go: add basic support for overlays
This CL adds basic support for listing packages with overlays.
The new cmd/go/internal/fs package adds an abstraction for communicating
with the file system that will open files according to their overlaid paths,
and provides functions to override those in the build context to open
overlaid files. There is also some support for executing builds on packages
with overlays. In cmd/go/internal/work.(*Builder).build, paths are mapped
to their overlaid paths before they are given as arguments to tools.
For #39958
Change-Id: I5ec0eb9ebbca303e2f1e7dbe22ec32613bc1fd17
Reviewed-on: https://go-review.googlesource.com/c/go/+/253747
Trust: Michael Matloob
Trust: Jay Conrod
Run-TryBot: Michael Matloob
TryBot-Result: Go Bot
Reviewed-by: Jay Conrod
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/cfg/cfg.go | 12 +
src/cmd/go/internal/envcmd/env.go | 5 +
src/cmd/go/internal/fsys/fsys.go | 426 ++++++++++++++++++++++++
src/cmd/go/internal/fsys/fsys_test.go | 479 +++++++++++++++++++++++++++
src/cmd/go/internal/imports/scan.go | 7 +-
src/cmd/go/internal/modload/import.go | 51 +--
src/cmd/go/internal/modload/init.go | 5 +
src/cmd/go/internal/search/search.go | 1 +
src/cmd/go/internal/work/build.go | 3 +
src/cmd/go/internal/work/gc.go | 23 +-
src/cmd/go/internal/work/init.go | 4 +
src/cmd/go/testdata/script/build_overlay.txt | 64 ++++
src/cmd/go/testdata/script/list_overlay.txt | 54 +++
13 files changed, 1081 insertions(+), 53 deletions(-)
create mode 100644 src/cmd/go/internal/fsys/fsys.go
create mode 100644 src/cmd/go/internal/fsys/fsys_test.go
create mode 100644 src/cmd/go/testdata/script/build_overlay.txt
create mode 100644 src/cmd/go/testdata/script/list_overlay.txt
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index ebbaf04115..9169c12d8f 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -11,6 +11,7 @@ import (
"fmt"
"go/build"
"internal/cfg"
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -18,6 +19,8 @@ import (
"strings"
"sync"
+ "cmd/go/internal/fsys"
+
"cmd/internal/objabi"
)
@@ -104,6 +107,15 @@ func defaultContext() build.Context {
// Nothing to do here.
}
+ ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
+ return fsys.Open(path)
+ }
+ ctxt.ReadDir = fsys.ReadDir
+ ctxt.IsDir = func(path string) bool {
+ isDir, err := fsys.IsDir(path)
+ return err == nil && isDir
+ }
+
return ctxt
}
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index ee0bb0d0b2..e1f2400f60 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -21,6 +21,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
@@ -197,6 +198,10 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
+ if err := fsys.Init(base.Cwd); err != nil {
+ base.Fatalf("go: %v", err)
+ }
+
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
// Only if we're listing all environment variables ("go env")
// or the variables being requested are in the extra list.
diff --git a/src/cmd/go/internal/fsys/fsys.go b/src/cmd/go/internal/fsys/fsys.go
new file mode 100644
index 0000000000..d64ce0aba1
--- /dev/null
+++ b/src/cmd/go/internal/fsys/fsys.go
@@ -0,0 +1,426 @@
+// Package fsys is an abstraction for reading files that
+// allows for virtual overlays on top of the files on disk.
+package fsys
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+)
+
+// OverlayFile is the path to a text file in the OverlayJSON format.
+// It is the value of the -overlay flag.
+var OverlayFile string
+
+// OverlayJSON is the format overlay files are expected to be in.
+// The Replace map maps from overlaid paths to replacement paths:
+// the Go command will forward all reads trying to open
+// each overlaid path to its replacement path, or consider the overlaid
+// path not to exist if the replacement path is empty.
+type OverlayJSON struct {
+ Replace map[string]string
+}
+
+type node struct {
+ actualFilePath string // empty if a directory
+ children map[string]*node // path element → file or directory
+}
+
+func (n *node) isDir() bool {
+ return n.actualFilePath == "" && n.children != nil
+}
+
+func (n *node) isDeleted() bool {
+ return n.actualFilePath == "" && n.children == nil
+}
+
+// TODO(matloob): encapsulate these in an io/fs-like interface
+var overlay map[string]*node // path -> file or directory node
+var cwd string // copy of base.Cwd to avoid dependency
+
+// Canonicalize a path for looking it up in the overlay.
+// Important: filepath.Join(cwd, path) doesn't always produce
+// the correct absolute path if path is relative, because on
+// Windows producing the correct absolute path requires making
+// a syscall. So this should only be used when looking up paths
+// in the overlay, or canonicalizing the paths in the overlay.
+func canonicalize(path string) string {
+ if path == "" {
+ return ""
+ }
+ if filepath.IsAbs(path) {
+ return filepath.Clean(path)
+ }
+
+ if v := filepath.VolumeName(cwd); v != "" && path[0] == filepath.Separator {
+ // On Windows filepath.Join(cwd, path) doesn't always work. In general
+ // filepath.Abs needs to make a syscall on Windows. Elsewhere in cmd/go
+ // use filepath.Join(cwd, path), but cmd/go specifically supports Windows
+ // paths that start with "\" which implies the path is relative to the
+ // volume of the working directory. See golang.org/issue/8130.
+ return filepath.Join(v, path)
+ }
+
+ // Make the path absolute.
+ return filepath.Join(cwd, path)
+}
+
+// Init initializes the overlay, if one is being used.
+func Init(wd string) error {
+ if overlay != nil {
+ // already initialized
+ return nil
+ }
+
+ cwd = wd
+
+ if OverlayFile == "" {
+ return nil
+ }
+
+ b, err := ioutil.ReadFile(OverlayFile)
+ if err != nil {
+ return fmt.Errorf("reading overlay file: %v", err)
+ }
+
+ var overlayJSON OverlayJSON
+ if err := json.Unmarshal(b, &overlayJSON); err != nil {
+ return fmt.Errorf("parsing overlay JSON: %v", err)
+ }
+
+ return initFromJSON(overlayJSON)
+}
+
+func initFromJSON(overlayJSON OverlayJSON) error {
+ // Canonicalize the paths in in the overlay map.
+ // Use reverseCanonicalized to check for collisions:
+ // no two 'from' paths should canonicalize to the same path.
+ overlay = make(map[string]*node)
+ reverseCanonicalized := make(map[string]string) // inverse of canonicalize operation, to check for duplicates
+ // Build a table of file and directory nodes from the replacement map.
+
+ // Remove any potential non-determinism from iterating over map by sorting it.
+ replaceFrom := make([]string, 0, len(overlayJSON.Replace))
+ for k := range overlayJSON.Replace {
+ replaceFrom = append(replaceFrom, k)
+ }
+ sort.Strings(replaceFrom)
+
+ for _, from := range replaceFrom {
+ to := overlayJSON.Replace[from]
+ // Canonicalize paths and check for a collision.
+ if from == "" {
+ return fmt.Errorf("empty string key in overlay file Replace map")
+ }
+ cfrom := canonicalize(from)
+ if to != "" {
+ // Don't canonicalize "", meaning to delete a file, because then it will turn into ".".
+ to = canonicalize(to)
+ }
+ if otherFrom, seen := reverseCanonicalized[cfrom]; seen {
+ return fmt.Errorf(
+ "paths %q and %q both canonicalize to %q in overlay file Replace map", otherFrom, from, cfrom)
+ }
+ reverseCanonicalized[cfrom] = from
+ from = cfrom
+
+ // Create node for overlaid file.
+ dir, base := filepath.Dir(from), filepath.Base(from)
+ if n, ok := overlay[from]; ok {
+ // All 'from' paths in the overlay are file paths. Since the from paths
+ // are in a map, they are unique, so if the node already exists we added
+ // it below when we create parent directory nodes. That is, that
+ // both a file and a path to one of its parent directories exist as keys
+ // in the Replace map.
+ //
+ // This only applies if the overlay directory has any files or directories
+ // in it: placeholder directories that only contain deleted files don't
+ // count. They are safe to be overwritten with actual files.
+ for _, f := range n.children {
+ if !f.isDeleted() {
+ return fmt.Errorf("invalid overlay: path %v is used as both file and directory", from)
+ }
+ }
+ }
+ overlay[from] = &node{actualFilePath: to}
+
+ // Add parent directory nodes to overlay structure.
+ childNode := overlay[from]
+ for {
+ dirNode := overlay[dir]
+ if dirNode == nil || dirNode.isDeleted() {
+ dirNode = &node{children: make(map[string]*node)}
+ overlay[dir] = dirNode
+ }
+ if childNode.isDeleted() {
+ // Only create one parent for a deleted file:
+ // the directory only conditionally exists if
+ // there are any non-deleted children, so
+ // we don't create their parents.
+ if dirNode.isDir() {
+ dirNode.children[base] = childNode
+ }
+ break
+ }
+ if !dirNode.isDir() {
+ // This path already exists as a file, so it can't be a parent
+ // directory. See comment at error above.
+ return fmt.Errorf("invalid overlay: path %v is used as both file and directory", dir)
+ }
+ dirNode.children[base] = childNode
+ parent := filepath.Dir(dir)
+ if parent == dir {
+ break // reached the top; there is no parent
+ }
+ dir, base = parent, filepath.Base(dir)
+ childNode = dirNode
+ }
+ }
+
+ return nil
+}
+
+// IsDir returns true if path is a directory on disk or in the
+// overlay.
+func IsDir(path string) (bool, error) {
+ path = canonicalize(path)
+
+ if _, ok := parentIsOverlayFile(path); ok {
+ return false, nil
+ }
+
+ if n, ok := overlay[path]; ok {
+ return n.isDir(), nil
+ }
+
+ fi, err := os.Stat(path)
+ if err != nil {
+ return false, err
+ }
+
+ return fi.IsDir(), nil
+}
+
+// parentIsOverlayFile returns whether name or any of
+// its parents are directories in the overlay, and the first parent found,
+// including name itself, that's a directory in the overlay.
+func parentIsOverlayFile(name string) (string, bool) {
+ if overlay != nil {
+ // Check if name can't possibly be a directory because
+ // it or one of its parents is overlaid with a file.
+ // TODO(matloob): Maybe save this to avoid doing it every time?
+ prefix := name
+ for {
+ node := overlay[prefix]
+ if node != nil && !node.isDir() {
+ return prefix, true
+ }
+ parent := filepath.Dir(prefix)
+ if parent == prefix {
+ break
+ }
+ prefix = parent
+ }
+ }
+
+ return "", false
+}
+
+// errNotDir is used to communicate from ReadDir to IsDirWithGoFiles
+// that the argument is not a directory, so that IsDirWithGoFiles doesn't
+// return an error.
+var errNotDir = errors.New("not a directory")
+
+// readDir reads a dir on disk, returning an error that is errNotDir if the dir is not a directory.
+// Unfortunately, the error returned by ioutil.ReadDir if dir is not a directory
+// can vary depending on the OS (Linux, Mac, Windows return ENOTDIR; BSD returns EINVAL).
+func readDir(dir string) ([]os.FileInfo, error) {
+ fis, err := ioutil.ReadDir(dir)
+ if err == nil {
+ return fis, nil
+ }
+
+ if os.IsNotExist(err) {
+ return nil, err
+ } else if dirfi, staterr := os.Stat(dir); staterr == nil && !dirfi.IsDir() {
+ return nil, &os.PathError{Op: "ReadDir", Path: dir, Err: errNotDir}
+ } else {
+ return nil, err
+ }
+}
+
+// ReadDir provides a slice of os.FileInfo entries corresponding
+// to the overlaid files in the directory.
+func ReadDir(dir string) ([]os.FileInfo, error) {
+ dir = canonicalize(dir)
+ if _, ok := parentIsOverlayFile(dir); ok {
+ return nil, &os.PathError{Op: "ReadDir", Path: dir, Err: errNotDir}
+ }
+
+ dirNode := overlay[dir]
+ if dirNode == nil {
+ return readDir(dir)
+ } else if dirNode.isDeleted() {
+ return nil, &os.PathError{Op: "ReadDir", Path: dir, Err: os.ErrNotExist}
+ }
+ diskfis, err := readDir(dir)
+ if err != nil && !os.IsNotExist(err) && !errors.Is(err, errNotDir) {
+ return nil, err
+ }
+
+ // Stat files in overlay to make composite list of fileinfos
+ files := make(map[string]os.FileInfo)
+ for _, f := range diskfis {
+ files[f.Name()] = f
+ }
+ for name, to := range dirNode.children {
+ switch {
+ case to.isDir():
+ files[name] = fakeDir(name)
+ case to.isDeleted():
+ delete(files, name)
+ default:
+ // This is a regular file.
+ f, err := os.Lstat(to.actualFilePath)
+ if err != nil {
+ files[name] = missingFile(name)
+ continue
+ } else if f.IsDir() {
+ return nil, fmt.Errorf("for overlay of %q to %q: overlay Replace entries can't point to dirctories",
+ filepath.Join(dir, name), to.actualFilePath)
+ }
+ // Add a fileinfo for the overlaid file, so that it has
+ // the original file's name, but the overlaid file's metadata.
+ files[name] = fakeFile{name, f}
+ }
+ }
+ sortedFiles := diskfis[:0]
+ for _, f := range files {
+ sortedFiles = append(sortedFiles, f)
+ }
+ sort.Slice(sortedFiles, func(i, j int) bool { return sortedFiles[i].Name() < sortedFiles[j].Name() })
+ return sortedFiles, nil
+}
+
+// OverlayPath returns the path to the overlaid contents of the
+// file, the empty string if the overlay deletes the file, or path
+// itself if the file is not in the overlay, the file is a directory
+// in the overlay, or there is no overlay.
+// It returns true if the path is overlaid with a regular file
+// or deleted, and false otherwise.
+func OverlayPath(path string) (string, bool) {
+ if p, ok := overlay[canonicalize(path)]; ok && !p.isDir() {
+ return p.actualFilePath, ok
+ }
+
+ return path, false
+}
+
+// Open opens the file at or overlaid on the given path.
+func Open(path string) (*os.File, error) {
+ cpath := canonicalize(path)
+ if node, ok := overlay[cpath]; ok {
+ if node.isDir() {
+ return nil, &os.PathError{Op: "Open", Path: path, Err: errors.New("fsys.Open doesn't support opening directories yet")}
+ }
+ return os.Open(node.actualFilePath)
+ } else if parent, ok := parentIsOverlayFile(filepath.Dir(cpath)); ok {
+ // The file is deleted explicitly in the Replace map,
+ // or implicitly because one of its parent directories was
+ // replaced by a file.
+ return nil, &os.PathError{
+ Op: "Open",
+ Path: path,
+ Err: fmt.Errorf("file %s does not exist: parent directory %s is replaced by a file in overlay", path, parent)}
+ } else {
+ return os.Open(cpath)
+ }
+}
+
+// IsDirWithGoFiles reports whether dir is a directory containing Go files
+// either on disk or in the overlay.
+func IsDirWithGoFiles(dir string) (bool, error) {
+ fis, err := ReadDir(dir)
+ if os.IsNotExist(err) || errors.Is(err, errNotDir) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+
+ var firstErr error
+ for _, fi := range fis {
+ if fi.IsDir() {
+ continue
+ }
+
+ // TODO(matloob): this enforces that the "from" in the map
+ // has a .go suffix, but the actual destination file
+ // doesn't need to have a .go suffix. Is this okay with the
+ // compiler?
+ if !strings.HasSuffix(fi.Name(), ".go") {
+ continue
+ }
+ if fi.Mode().IsRegular() {
+ return true, nil
+ }
+
+ // fi is the result of an Lstat, so it doesn't follow symlinks.
+ // But it's okay if the file is a symlink pointing to a regular
+ // file, so use os.Stat to follow symlinks and check that.
+ actualFilePath, _ := OverlayPath(filepath.Join(dir, fi.Name()))
+ if fi, err := os.Stat(actualFilePath); err == nil && fi.Mode().IsRegular() {
+ return true, nil
+ } else if err != nil && firstErr == nil {
+ firstErr = err
+ }
+ }
+
+ // No go files found in directory.
+ return false, firstErr
+}
+
+// fakeFile provides an os.FileInfo implementation for an overlaid file,
+// so that the file has the name of the overlaid file, but takes all
+// other characteristics of the replacement file.
+type fakeFile struct {
+ name string
+ real os.FileInfo
+}
+
+func (f fakeFile) Name() string { return f.name }
+func (f fakeFile) Size() int64 { return f.real.Size() }
+func (f fakeFile) Mode() os.FileMode { return f.real.Mode() }
+func (f fakeFile) ModTime() time.Time { return f.real.ModTime() }
+func (f fakeFile) IsDir() bool { return f.real.IsDir() }
+func (f fakeFile) Sys() interface{} { return f.real.Sys() }
+
+// missingFile provides an os.FileInfo for an overlaid file where the
+// destination file in the overlay doesn't exist. It returns zero values
+// for the fileInfo methods other than Name, set to the file's name, and Mode
+// set to ModeIrregular.
+type missingFile string
+
+func (f missingFile) Name() string { return string(f) }
+func (f missingFile) Size() int64 { return 0 }
+func (f missingFile) Mode() os.FileMode { return os.ModeIrregular }
+func (f missingFile) ModTime() time.Time { return time.Unix(0, 0) }
+func (f missingFile) IsDir() bool { return false }
+func (f missingFile) Sys() interface{} { return nil }
+
+// fakeDir provides an os.FileInfo implementation for directories that are
+// implicitly created by overlaid files. Each directory in the
+// path of an overlaid file is considered to exist in the overlay filesystem.
+type fakeDir string
+
+func (f fakeDir) Name() string { return string(f) }
+func (f fakeDir) Size() int64 { return 0 }
+func (f fakeDir) Mode() os.FileMode { return os.ModeDir | 0500 }
+func (f fakeDir) ModTime() time.Time { return time.Unix(0, 0) }
+func (f fakeDir) IsDir() bool { return true }
+func (f fakeDir) Sys() interface{} { return nil }
diff --git a/src/cmd/go/internal/fsys/fsys_test.go b/src/cmd/go/internal/fsys/fsys_test.go
new file mode 100644
index 0000000000..4b53059427
--- /dev/null
+++ b/src/cmd/go/internal/fsys/fsys_test.go
@@ -0,0 +1,479 @@
+package fsys
+
+import (
+ "cmd/go/internal/txtar"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+// initOverlay resets the overlay state to reflect the config.
+// config should be a text archive string. The comment is the overlay config
+// json, and the files, in the archive are laid out in a temp directory
+// that cwd is set to.
+func initOverlay(t *testing.T, config string) {
+ t.Helper()
+
+ // Create a temporary directory and chdir to it.
+ prevwd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cwd = t.TempDir()
+ if err := os.Chdir(cwd); err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ overlay = nil
+ if err := os.Chdir(prevwd); err != nil {
+ t.Fatal(err)
+ }
+ })
+
+ a := txtar.Parse([]byte(config))
+ for _, f := range a.Files {
+ name := filepath.Join(cwd, f.Name)
+ if err := os.MkdirAll(filepath.Dir(name), 0777); err != nil {
+ t.Fatal(err)
+ }
+ if err := ioutil.WriteFile(name, f.Data, 0666); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ var overlayJSON OverlayJSON
+ if err := json.Unmarshal(a.Comment, &overlayJSON); err != nil {
+ t.Fatal(fmt.Errorf("parsing overlay JSON: %v", err))
+ }
+
+ initFromJSON(overlayJSON)
+}
+
+func TestIsDir(t *testing.T) {
+ initOverlay(t, `
+{
+ "Replace": {
+ "subdir2/file2.txt": "overlayfiles/subdir2_file2.txt",
+ "subdir4": "overlayfiles/subdir4",
+ "subdir3/file3b.txt": "overlayfiles/subdir3_file3b.txt",
+ "subdir5": "",
+ "subdir6": ""
+ }
+}
+-- subdir1/file1.txt --
+
+-- subdir3/file3a.txt --
+33
+-- subdir4/file4.txt --
+444
+-- overlayfiles/subdir2_file2.txt --
+2
+-- overlayfiles/subdir3_file3b.txt --
+66666
+-- overlayfiles/subdir4 --
+x
+-- subdir6/file6.txt --
+six
+`)
+
+ testCases := []struct {
+ path string
+ want, wantErr bool
+ }{
+ {"", true, true},
+ {".", true, false},
+ {cwd, true, false},
+ {cwd + string(filepath.Separator), true, false},
+ // subdir1 is only on disk
+ {filepath.Join(cwd, "subdir1"), true, false},
+ {"subdir1", true, false},
+ {"subdir1" + string(filepath.Separator), true, false},
+ {"subdir1/file1.txt", false, false},
+ {"subdir1/doesntexist.txt", false, true},
+ {"doesntexist", false, true},
+ // subdir2 is only in overlay
+ {filepath.Join(cwd, "subdir2"), true, false},
+ {"subdir2", true, false},
+ {"subdir2" + string(filepath.Separator), true, false},
+ {"subdir2/file2.txt", false, false},
+ {"subdir2/doesntexist.txt", false, true},
+ // subdir3 has files on disk and in overlay
+ {filepath.Join(cwd, "subdir3"), true, false},
+ {"subdir3", true, false},
+ {"subdir3" + string(filepath.Separator), true, false},
+ {"subdir3/file3a.txt", false, false},
+ {"subdir3/file3b.txt", false, false},
+ {"subdir3/doesntexist.txt", false, true},
+ // subdir4 is overlaid with a file
+ {filepath.Join(cwd, "subdir4"), false, false},
+ {"subdir4", false, false},
+ {"subdir4" + string(filepath.Separator), false, false},
+ {"subdir4/file4.txt", false, false},
+ {"subdir4/doesntexist.txt", false, false},
+ // subdir5 doesn't exist, and is overlaid with a "delete" entry
+ {filepath.Join(cwd, "subdir5"), false, false},
+ {"subdir5", false, false},
+ {"subdir5" + string(filepath.Separator), false, false},
+ {"subdir5/file5.txt", false, false},
+ {"subdir5/doesntexist.txt", false, false},
+ // subdir6 does exist, and is overlaid with a "delete" entry
+ {filepath.Join(cwd, "subdir6"), false, false},
+ {"subdir6", false, false},
+ {"subdir6" + string(filepath.Separator), false, false},
+ {"subdir6/file6.txt", false, false},
+ {"subdir6/doesntexist.txt", false, false},
+ }
+
+ for _, tc := range testCases {
+ got, err := IsDir(tc.path)
+ if err != nil {
+ if !tc.wantErr {
+ t.Errorf("IsDir(%q): got error with string %q, want no error", tc.path, err.Error())
+ }
+ continue
+ }
+ if tc.wantErr {
+ t.Errorf("IsDir(%q): got no error, want error", tc.path)
+ }
+ if tc.want != got {
+ t.Errorf("IsDir(%q) = %v, want %v", tc.path, got, tc.want)
+ }
+ }
+}
+
+func TestReadDir(t *testing.T) {
+ initOverlay(t, `
+{
+ "Replace": {
+ "subdir2/file2.txt": "overlayfiles/subdir2_file2.txt",
+ "subdir4": "overlayfiles/subdir4",
+ "subdir3/file3b.txt": "overlayfiles/subdir3_file3b.txt",
+ "subdir5": "",
+ "subdir6/asubsubdir/afile.txt": "overlayfiles/subdir6_asubsubdir_afile.txt",
+ "subdir6/asubsubdir/zfile.txt": "overlayfiles/subdir6_asubsubdir_zfile.txt",
+ "subdir6/zsubsubdir/file.txt": "overlayfiles/subdir6_zsubsubdir_file.txt",
+ "subdir7/asubsubdir/file.txt": "overlayfiles/subdir7_asubsubdir_file.txt",
+ "subdir7/zsubsubdir/file.txt": "overlayfiles/subdir7_zsubsubdir_file.txt",
+ "subdir8/doesntexist": "this_file_doesnt_exist_anywhere",
+ "other/pointstodir": "overlayfiles/this_is_a_directory",
+ "parentoverwritten/subdir1": "overlayfiles/parentoverwritten_subdir1",
+ "subdir9/this_file_is_overlaid.txt": "overlayfiles/subdir9_this_file_is_overlaid.txt",
+ "subdir10/only_deleted_file.txt": "",
+ "subdir11/deleted.txt": "",
+ "subdir11": "overlayfiles/subdir11",
+ "textfile.txt/file.go": "overlayfiles/textfile_txt_file.go"
+ }
+}
+-- subdir1/file1.txt --
+
+-- subdir3/file3a.txt --
+33
+-- subdir4/file4.txt --
+444
+-- subdir6/file.txt --
+-- subdir6/asubsubdir/file.txt --
+-- subdir6/anothersubsubdir/file.txt --
+-- subdir9/this_file_is_overlaid.txt --
+-- subdir10/only_deleted_file.txt --
+this will be deleted in overlay
+-- subdir11/deleted.txt --
+-- parentoverwritten/subdir1/subdir2/subdir3/file.txt --
+-- textfile.txt --
+this will be overridden by textfile.txt/file.go
+-- overlayfiles/subdir2_file2.txt --
+2
+-- overlayfiles/subdir3_file3b.txt --
+66666
+-- overlayfiles/subdir4 --
+x
+-- overlayfiles/subdir6_asubsubdir_afile.txt --
+-- overlayfiles/subdir6_asubsubdir_zfile.txt --
+-- overlayfiles/subdir6_zsubsubdir_file.txt --
+-- overlayfiles/subdir7_asubsubdir_file.txt --
+-- overlayfiles/subdir7_zsubsubdir_file.txt --
+-- overlayfiles/parentoverwritten_subdir1 --
+x
+-- overlayfiles/subdir9_this_file_is_overlaid.txt --
+99999999
+-- overlayfiles/subdir11 --
+-- overlayfiles/this_is_a_directory/file.txt --
+-- overlayfiles/textfile_txt_file.go --
+x
+`)
+
+ testCases := map[string][]struct {
+ name string
+ size int64
+ isDir bool
+ }{
+ ".": {
+ {"other", 0, true},
+ {"overlayfiles", 0, true},
+ {"parentoverwritten", 0, true},
+ {"subdir1", 0, true},
+ {"subdir10", 0, true},
+ {"subdir11", 0, false},
+ {"subdir2", 0, true},
+ {"subdir3", 0, true},
+ {"subdir4", 2, false},
+ // no subdir5.
+ {"subdir6", 0, true},
+ {"subdir7", 0, true},
+ {"subdir8", 0, true},
+ {"subdir9", 0, true},
+ {"textfile.txt", 0, true},
+ },
+ "subdir1": {{"file1.txt", 1, false}},
+ "subdir2": {{"file2.txt", 2, false}},
+ "subdir3": {{"file3a.txt", 3, false}, {"file3b.txt", 6, false}},
+ "subdir6": {
+ {"anothersubsubdir", 0, true},
+ {"asubsubdir", 0, true},
+ {"file.txt", 0, false},
+ {"zsubsubdir", 0, true},
+ },
+ "subdir6/asubsubdir": {{"afile.txt", 0, false}, {"file.txt", 0, false}, {"zfile.txt", 0, false}},
+ "subdir8": {{"doesntexist", 0, false}}, // entry is returned even if destination file doesn't exist
+ // check that read dir actually redirects files that already exist
+ // the original this_file_is_overlaid.txt is empty
+ "subdir9": {{"this_file_is_overlaid.txt", 9, false}},
+ "subdir10": {},
+ "parentoverwritten": {{"subdir1", 2, false}},
+ "textfile.txt": {{"file.go", 2, false}},
+ }
+
+ for dir, want := range testCases {
+ fis, err := ReadDir(dir)
+ if err != nil {
+ t.Fatalf("ReadDir(%q): got error %q, want no error", dir, err)
+ }
+ if len(fis) != len(want) {
+ t.Fatalf("ReadDir(%q) result: got %v entries; want %v entries", dir, len(fis), len(want))
+ }
+ for i := range fis {
+ if fis[i].Name() != want[i].name {
+ t.Fatalf("ReadDir(%q) entry %v: got Name() = %v, want %v", dir, i, fis[i].Name(), want[i].name)
+ }
+ if fis[i].IsDir() != want[i].isDir {
+ t.Fatalf("ReadDir(%q) entry %v: got IsDir() = %v, want %v", dir, i, fis[i].IsDir(), want[i].isDir)
+ }
+ if want[i].isDir {
+ // We don't try to get size right for directories
+ continue
+ }
+ if fis[i].Size() != want[i].size {
+ t.Fatalf("ReadDir(%q) entry %v: got Size() = %v, want %v", dir, i, fis[i].Size(), want[i].size)
+ }
+ }
+ }
+
+ errCases := []string{
+ "subdir1/file1.txt", // regular file on disk
+ "subdir2/file2.txt", // regular file in overlay
+ "subdir4", // directory overlaid with regular file
+ "subdir5", // directory deleted in overlay
+ "parentoverwritten/subdir1/subdir2/subdir3", // parentoverwritten/subdir1 overlaid with regular file
+ "parentoverwritten/subdir1/subdir2", // parentoverwritten/subdir1 overlaid with regular file
+ "subdir11", // directory with deleted child, overlaid with regular file
+ "other/pointstodir",
+ }
+
+ for _, dir := range errCases {
+ _, gotErr := ReadDir(dir)
+ if gotErr == nil {
+ t.Errorf("ReadDir(%q): got no error, want error", dir)
+ } else if _, ok := gotErr.(*os.PathError); !ok {
+ t.Errorf("ReadDir(%q): got error with string %q and type %T, want os.PathError", dir, gotErr.Error(), gotErr)
+ }
+ }
+}
+
+func TestOverlayPath(t *testing.T) {
+ initOverlay(t, `
+{
+ "Replace": {
+ "subdir2/file2.txt": "overlayfiles/subdir2_file2.txt",
+ "subdir3/doesntexist": "this_file_doesnt_exist_anywhere",
+ "subdir4/this_file_is_overlaid.txt": "overlayfiles/subdir4_this_file_is_overlaid.txt",
+ "subdir5/deleted.txt": "",
+ "parentoverwritten/subdir1": ""
+ }
+}
+-- subdir1/file1.txt --
+file 1
+-- subdir4/this_file_is_overlaid.txt --
+these contents are replaced by the overlay
+-- parentoverwritten/subdir1/subdir2/subdir3/file.txt --
+-- subdir5/deleted.txt --
+deleted
+-- overlayfiles/subdir2_file2.txt --
+file 2
+-- overlayfiles/subdir4_this_file_is_overlaid.txt --
+99999999
+`)
+
+ testCases := []struct {
+ path string
+ wantPath string
+ wantOK bool
+ }{
+ {"subdir1/file1.txt", "subdir1/file1.txt", false},
+ // OverlayPath returns false for directories
+ {"subdir2", "subdir2", false},
+ {"subdir2/file2.txt", filepath.Join(cwd, "overlayfiles/subdir2_file2.txt"), true},
+ // OverlayPath doesn't stat a file to see if it exists, so it happily returns
+ // the 'to' path and true even if the 'to' path doesn't exist on disk.
+ {"subdir3/doesntexist", filepath.Join(cwd, "this_file_doesnt_exist_anywhere"), true},
+ // Like the subdir2/file2.txt case above, but subdir4 exists on disk, but subdir2 does not.
+ {"subdir4/this_file_is_overlaid.txt", filepath.Join(cwd, "overlayfiles/subdir4_this_file_is_overlaid.txt"), true},
+ {"subdir5", "subdir5", false},
+ {"subdir5/deleted.txt", "", true},
+ }
+
+ for _, tc := range testCases {
+ gotPath, gotOK := OverlayPath(tc.path)
+ if gotPath != tc.wantPath || gotOK != tc.wantOK {
+ t.Errorf("OverlayPath(%q): got %v, %v; want %v, %v",
+ tc.path, gotPath, gotOK, tc.wantPath, tc.wantOK)
+ }
+ }
+}
+
+func TestOpen(t *testing.T) {
+ initOverlay(t, `
+{
+ "Replace": {
+ "subdir2/file2.txt": "overlayfiles/subdir2_file2.txt",
+ "subdir3/doesntexist": "this_file_doesnt_exist_anywhere",
+ "subdir4/this_file_is_overlaid.txt": "overlayfiles/subdir4_this_file_is_overlaid.txt",
+ "subdir5/deleted.txt": "",
+ "parentoverwritten/subdir1": "",
+ "childoverlay/subdir1.txt/child.txt": "overlayfiles/child.txt",
+ "subdir11/deleted.txt": "",
+ "subdir11": "overlayfiles/subdir11",
+ "parentdeleted": "",
+ "parentdeleted/file.txt": "overlayfiles/parentdeleted_file.txt"
+ }
+}
+-- subdir11/deleted.txt --
+-- subdir1/file1.txt --
+file 1
+-- subdir4/this_file_is_overlaid.txt --
+these contents are replaced by the overlay
+-- parentoverwritten/subdir1/subdir2/subdir3/file.txt --
+-- childoverlay/subdir1.txt --
+this file doesn't exist because the path
+childoverlay/subdir1.txt/child.txt is in the overlay
+-- subdir5/deleted.txt --
+deleted
+-- parentdeleted --
+this will be deleted so that parentdeleted/file.txt can exist
+-- overlayfiles/subdir2_file2.txt --
+file 2
+-- overlayfiles/subdir4_this_file_is_overlaid.txt --
+99999999
+-- overlayfiles/child.txt --
+-- overlayfiles/subdir11 --
+11
+-- overlayfiles/parentdeleted_file.txt --
+this can exist because the parent directory is deleted
+`)
+
+ testCases := []struct {
+ path string
+ wantContents string
+ isErr bool
+ }{
+ {"subdir1/file1.txt", "file 1\n", false},
+ {"subdir2/file2.txt", "file 2\n", false},
+ {"subdir3/doesntexist", "", true},
+ {"subdir4/this_file_is_overlaid.txt", "99999999\n", false},
+ {"subdir5/deleted.txt", "", true},
+ {"parentoverwritten/subdir1/subdir2/subdir3/file.txt", "", true},
+ {"childoverlay/subdir1.txt", "", true},
+ {"subdir11", "11\n", false},
+ {"parentdeleted/file.txt", "this can exist because the parent directory is deleted\n", false},
+ }
+
+ for _, tc := range testCases {
+ f, err := Open(tc.path)
+ if tc.isErr {
+ if err == nil {
+ f.Close()
+ t.Errorf("Open(%q): got no error, but want error", tc.path)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("Open(%q): got error %v, want nil", tc.path, err)
+ continue
+ }
+ contents, err := ioutil.ReadAll(f)
+ if err != nil {
+ t.Errorf("unexpected error reading contents of file: %v", err)
+ }
+ if string(contents) != tc.wantContents {
+ t.Errorf("contents of file opened with Open(%q): got %q, want %q",
+ tc.path, contents, tc.wantContents)
+ }
+ f.Close()
+ }
+}
+
+func TestIsDirWithGoFiles(t *testing.T) {
+ initOverlay(t, `
+{
+ "Replace": {
+ "goinoverlay/file.go": "dummy",
+ "directory/removed/by/file": "dummy",
+ "directory_with_go_dir/dir.go/file.txt": "dummy",
+ "otherdirectory/deleted.go": "",
+ "nonexistentdirectory/deleted.go": "",
+ "textfile.txt/file.go": "dummy"
+ }
+}
+-- dummy --
+a destination file for the overlay entries to point to
+contents don't matter for this test
+-- nogo/file.txt --
+-- goondisk/file.go --
+-- goinoverlay/file.txt --
+-- directory/removed/by/file/in/overlay/file.go --
+-- otherdirectory/deleted.go --
+-- textfile.txt --
+`)
+
+ testCases := []struct {
+ dir string
+ want bool
+ wantErr bool
+ }{
+ {"nogo", false, false},
+ {"goondisk", true, false},
+ {"goinoverlay", true, false},
+ {"directory/removed/by/file/in/overlay", false, false},
+ {"directory_with_go_dir", false, false},
+ {"otherdirectory", false, false},
+ {"nonexistentdirectory", false, false},
+ {"textfile.txt", true, false},
+ }
+
+ for _, tc := range testCases {
+ got, gotErr := IsDirWithGoFiles(tc.dir)
+ if tc.wantErr {
+ if gotErr == nil {
+ t.Errorf("IsDirWithGoFiles(%q): got %v, %v; want non-nil error", tc.dir, got, gotErr)
+ }
+ continue
+ }
+ if gotErr != nil {
+ t.Errorf("IsDirWithGoFiles(%q): got %v, %v; want nil error", tc.dir, got, gotErr)
+ }
+ if got != tc.want {
+ t.Errorf("IsDirWithGoFiles(%q) = %v; want %v", tc.dir, got, tc.want)
+ }
+ }
+}
diff --git a/src/cmd/go/internal/imports/scan.go b/src/cmd/go/internal/imports/scan.go
index 3d9b6132b1..42ee49aaaa 100644
--- a/src/cmd/go/internal/imports/scan.go
+++ b/src/cmd/go/internal/imports/scan.go
@@ -6,16 +6,17 @@ package imports
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
+
+ "cmd/go/internal/fsys"
)
func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
- infos, err := ioutil.ReadDir(dir)
+ infos, err := fsys.ReadDir(dir)
if err != nil {
return nil, nil, err
}
@@ -49,7 +50,7 @@ func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]stri
numFiles := 0
Files:
for _, name := range files {
- r, err := os.Open(name)
+ r, err := fsys.Open(name)
if err != nil {
return nil, nil, err
}
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index c36c8bd29b..3642de851a 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -17,6 +17,7 @@ import (
"time"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/modfetch"
"cmd/go/internal/par"
"cmd/go/internal/search"
@@ -438,57 +439,9 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
// We don't care about build tags, not even "+build ignore".
// We're just looking for a plausible directory.
res := haveGoFilesCache.Do(dir, func() interface{} {
- ok, err := isDirWithGoFiles(dir)
+ ok, err := fsys.IsDirWithGoFiles(dir)
return goFilesEntry{haveGoFiles: ok, err: err}
}).(goFilesEntry)
return dir, res.haveGoFiles, res.err
}
-
-func isDirWithGoFiles(dir string) (bool, error) {
- f, err := os.Open(dir)
- if err != nil {
- if os.IsNotExist(err) {
- return false, nil
- }
- return false, err
- }
- defer f.Close()
-
- names, firstErr := f.Readdirnames(-1)
- if firstErr != nil {
- if fi, err := f.Stat(); err == nil && !fi.IsDir() {
- return false, nil
- }
-
- // Rewrite the error from ReadDirNames to include the path if not present.
- // See https://golang.org/issue/38923.
- var pe *os.PathError
- if !errors.As(firstErr, &pe) {
- firstErr = &os.PathError{Op: "readdir", Path: dir, Err: firstErr}
- }
- }
-
- for _, name := range names {
- if strings.HasSuffix(name, ".go") {
- info, err := os.Stat(filepath.Join(dir, name))
- if err == nil && info.Mode().IsRegular() {
- // If any .go source file exists, the package exists regardless of
- // errors for other source files. Leave further error reporting for
- // later.
- return true, nil
- }
- if firstErr == nil {
- if os.IsNotExist(err) {
- // If the file was concurrently deleted, or was a broken symlink,
- // convert the error to an opaque error instead of one matching
- // os.IsNotExist.
- err = errors.New(err.Error())
- }
- firstErr = err
- }
- }
- }
-
- return false, firstErr
-}
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 3344242489..e1b784860b 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -22,6 +22,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modconv"
"cmd/go/internal/modfetch"
@@ -132,6 +133,10 @@ func Init() {
return
}
+ if err := fsys.Init(base.Cwd); err != nil {
+ base.Fatalf("go: %v", err)
+ }
+
// Disable any prompting for passwords by Git.
// Only has an effect for 2.3.0 or later, but avoiding
// the prompt in earlier versions is just too hard.
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
index 4efef24152..868dbf5f9d 100644
--- a/src/cmd/go/internal/search/search.go
+++ b/src/cmd/go/internal/search/search.go
@@ -264,6 +264,7 @@ func (m *Match) MatchDirs() {
}
err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ // TODO(#39958): Handle walk for overlays.
if err != nil {
return err // Likely a permission error, which could interfere with matching.
}
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 86423f118c..21342ac8ba 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -19,6 +19,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/load"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
@@ -277,6 +278,8 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
+ cmd.Flag.StringVar(&fsys.OverlayFile, "overlay", "", "")
+
// Undocumented, unstable debugging flags.
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "")
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index d76574932e..1f15654c79 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -18,6 +18,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/internal/objabi"
@@ -145,7 +146,25 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s
}
for _, f := range gofiles {
- args = append(args, mkAbs(p.Dir, f))
+ f := mkAbs(p.Dir, f)
+
+ // Handle overlays. Convert path names using OverlayPath
+ // so these paths can be handed directly to tools.
+ // Deleted files won't show up in when scanning directories earlier,
+ // so OverlayPath will never return "" (meaning a deleted file) here.
+ // TODO(#39958): Handle -trimprefix and other cases where
+ // tools depend on the names of the files that are passed in.
+ // TODO(#39958): Handle cases where the package directory
+ // doesn't exist on disk (this can happen when all the package's
+ // files are in an overlay): the code expects the package directory
+ // to exist and runs some tools in that directory.
+ // TODO(#39958): Process the overlays when the
+ // gofiles, cgofiles, cfiles, sfiles, and cxxfiles variables are
+ // created in (*Builder).build. Doing that requires rewriting the
+ // code that uses those values to expect absolute paths.
+ f, _ = fsys.OverlayPath(f)
+
+ args = append(args, f)
}
output, err = b.runOut(a, p.Dir, nil, args...)
@@ -247,6 +266,8 @@ func (a *Action) trimpath() string {
}
}
+ // TODO(#39958): Add rewrite rules for overlaid files.
+
return rewrite
}
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index b0d6133768..bab1935aca 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -9,6 +9,7 @@ package work
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/modload"
"cmd/internal/objabi"
"cmd/internal/sys"
@@ -24,6 +25,9 @@ func BuildInit() {
modload.Init()
instrumentInit()
buildModeInit()
+ if err := fsys.Init(base.Cwd); err != nil {
+ base.Fatalf("go: %v", err)
+ }
// Make sure -pkgdir is absolute, because we run commands
// in different directories.
diff --git a/src/cmd/go/testdata/script/build_overlay.txt b/src/cmd/go/testdata/script/build_overlay.txt
new file mode 100644
index 0000000000..3b039901fa
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_overlay.txt
@@ -0,0 +1,64 @@
+[short] skip
+
+# Test building in overlays.
+# TODO(matloob): add a test case where the destination file in the replace map
+# isn't a go file. Either completely exclude that case in fs.IsDirWithGoFiles
+# if the compiler doesn't allow it, or test that it works all the way.
+
+# The main package (m) is contained in an overlay. It imports m/dir2 which has one
+# file in an overlay and one file outside the overlay, which in turn imports m/dir,
+# which only has source files in the overlay.
+
+! go build .
+go build -overlay overlay.json -o main$GOEXE .
+exec ./main$goexe
+stdout '^hello$'
+
+-- go.mod --
+// TODO(matloob): how do overlays work with go.mod (especially if mod=readonly)
+module m
+
+go 1.16
+
+-- dir2/h.go --
+package dir2
+
+func PrintMessage() {
+ printMessage()
+}
+-- dir/foo.txt --
+The build action code currently expects the package directory
+to exist, so it can run the compiler in that directory.
+TODO(matloob): Remove this requirement.
+-- overlay.json --
+{
+ "Replace": {
+ "f.go": "overlay/f.go",
+ "dir/g.go": "overlay/dir_g.go",
+ "dir2/i.go": "overlay/dir2_i.go"
+ }
+}
+-- overlay/f.go --
+package main
+
+import "m/dir2"
+
+func main() {
+ dir2.PrintMessage()
+}
+-- overlay/dir_g.go --
+package dir
+
+import "fmt"
+
+func PrintMessage() {
+ fmt.Println("hello")
+}
+-- overlay/dir2_i.go --
+package dir2
+
+import "m/dir"
+
+func printMessage() {
+ dir.PrintMessage()
+}
diff --git a/src/cmd/go/testdata/script/list_overlay.txt b/src/cmd/go/testdata/script/list_overlay.txt
new file mode 100644
index 0000000000..7d0e3c2c81
--- /dev/null
+++ b/src/cmd/go/testdata/script/list_overlay.txt
@@ -0,0 +1,54 @@
+# Test listing with overlays
+
+# Overlay in an existing directory
+go list -overlay overlay.json -f '{{.GoFiles}}' .
+stdout '^\[f.go\]$'
+
+# Overlays in a non-existing directory
+go list -overlay overlay.json -f '{{.GoFiles}}' ./dir
+stdout '^\[g.go\]$'
+
+# Overlays in an existing directory with already existing files
+go list -overlay overlay.json -f '{{.GoFiles}}' ./dir2
+stdout '^\[h.go i.go\]$'
+
+# Overlay that removes a file from a directory
+! go list ./dir3 # contains a file without a package statement
+go list -overlay overlay.json -f '{{.GoFiles}}' ./dir3 # overlay removes that file
+
+# TODO(#39958): assembly files, C files, files that require cgo preprocessing
+
+-- go.mod --
+// TODO(#39958): Support and test overlays including go.mod itself (especially if mod=readonly)
+module m
+
+go 1.16
+
+-- dir2/h.go --
+package dir2
+
+-- dir3/good.go --
+package dir3
+-- dir3/bad.go --
+// no package statement
+-- overlay.json --
+{
+ "Replace": {
+ "f.go": "overlay/f.go",
+ "dir/g.go": "overlay/dir_g.go",
+ "dir2/i.go": "overlay/dir2_i.go",
+ "dir3/bad.go": ""
+ }
+}
+-- overlay/f.go --
+package m
+
+func f() {
+}
+-- overlay/dir_g.go --
+package m
+
+func g() {
+}
+-- overlay/dir2_i.go --
+package dir2
--
cgit v1.2.3-54-g00ecf
From 1fb149fd640f2e83f17206aa6eb530d664b0b5ed Mon Sep 17 00:00:00 2001
From: witchard
Date: Fri, 25 Sep 2020 14:09:42 +0000
Subject: cmd/go/internal/get: improve -insecure deprecation docs
Updates #37519
Change-Id: I212607f1839b729d7da24b1258e56997b13ad830
GitHub-Last-Rev: db6d3c835bdf867a0b18f115276210e3a05902ed
GitHub-Pull-Request: golang/go#41613
Reviewed-on: https://go-review.googlesource.com/c/go/+/257157
Run-TryBot: Bryan C. Mills
TryBot-Result: Go Bot
Trust: Jay Conrod
Trust: Bryan C. Mills
Reviewed-by: Bryan C. Mills
Reviewed-by: Jay Conrod
---
doc/go1.16.html | 13 +++++++------
src/cmd/go/alldocs.go | 15 +++++++--------
src/cmd/go/internal/get/get.go | 4 ++--
src/cmd/go/internal/modget/get.go | 11 +++++------
4 files changed, 21 insertions(+), 22 deletions(-)
diff --git a/doc/go1.16.html b/doc/go1.16.html
index f7bcb9e94f..2fb7222482 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -90,12 +90,13 @@ Do not send CLs removing the interior tags from such phrases.
The go
get
-insecure
flag is
- deprecated and will be removed in a future version. The GOINSECURE
- environment variable should be used instead, since it provides control
- over which modules may be retrieved using an insecure scheme. Unlike the
- -insecure
flag, GOINSECURE
does not disable module
- sum validation using the checksum database. The GOPRIVATE
or
- GONOSUMDB
environment variables may be used instead.
+ deprecated and will be removed in a future version. This flag permits
+ fetching from repositories and resolving custom domains using insecure
+ schemes such as HTTP, and also bypassess module sum validation using the
+ checksum database. To permit the use of insecure schemes, use the
+ GOINSECURE
environment variable instead. To bypass module
+ sum validation, use GOPRIVATE
or GONOSUMDB
.
+ See go
help
environment
for details.
The all
pattern
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 500682ed02..14840efb22 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -662,13 +662,12 @@
// this automatically as well.
//
// The -insecure flag permits fetching from repositories and resolving
-// custom domains using insecure schemes such as HTTP. Use with caution.
+// custom domains using insecure schemes such as HTTP, and also bypassess
+// module sum validation using the checksum database. Use with caution.
// This flag is deprecated and will be removed in a future version of go.
-// The GOINSECURE environment variable is usually a better alternative, since
-// it provides control over which modules may be retrieved using an insecure
-// scheme. It should be noted that the -insecure flag also turns the module
-// checksum validation off. GOINSECURE does not do that, use GONOSUMDB.
-// See 'go help environment' for details.
+// To permit the use of insecure schemes, use the GOINSECURE environment
+// variable instead. To bypass module sum validation, use GOPRIVATE or
+// GONOSUMDB. See 'go help environment' for details.
//
// The second step is to download (if needed), build, and install
// the named packages.
@@ -2211,8 +2210,8 @@
// The -insecure flag permits fetching from repositories and resolving
// custom domains using insecure schemes such as HTTP. Use with caution.
// This flag is deprecated and will be removed in a future version of go.
-// The GOINSECURE environment variable is usually a better alternative, since
-// it provides control over which modules may be retrieved using an insecure
+// The GOINSECURE environment variable should be used instead, since it
+// provides control over which packages may be retrieved using an insecure
// scheme. See 'go help environment' for details.
//
// The -t flag instructs get to also download the packages required to build
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index ed2786879c..268962eca8 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -46,8 +46,8 @@ before resolving dependencies or building the code.
The -insecure flag permits fetching from repositories and resolving
custom domains using insecure schemes such as HTTP. Use with caution.
This flag is deprecated and will be removed in a future version of go.
-The GOINSECURE environment variable is usually a better alternative, since
-it provides control over which modules may be retrieved using an insecure
+The GOINSECURE environment variable should be used instead, since it
+provides control over which packages may be retrieved using an insecure
scheme. See 'go help environment' for details.
The -t flag instructs get to also download the packages required to build
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index f1cf8b17a8..ea0e99af7d 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -115,13 +115,12 @@ require downgrading other dependencies, and 'go get' does
this automatically as well.
The -insecure flag permits fetching from repositories and resolving
-custom domains using insecure schemes such as HTTP. Use with caution.
+custom domains using insecure schemes such as HTTP, and also bypassess
+module sum validation using the checksum database. Use with caution.
This flag is deprecated and will be removed in a future version of go.
-The GOINSECURE environment variable is usually a better alternative, since
-it provides control over which modules may be retrieved using an insecure
-scheme. It should be noted that the -insecure flag also turns the module
-checksum validation off. GOINSECURE does not do that, use GONOSUMDB.
-See 'go help environment' for details.
+To permit the use of insecure schemes, use the GOINSECURE environment
+variable instead. To bypass module sum validation, use GOPRIVATE or
+GONOSUMDB. See 'go help environment' for details.
The second step is to download (if needed), build, and install
the named packages.
--
cgit v1.2.3-54-g00ecf
From bdab5df40f474c7768a945ef4fcf5aab634f7af5 Mon Sep 17 00:00:00 2001
From: Lynn Boger
Date: Fri, 2 Oct 2020 17:51:13 -0400
Subject: cmd/compile,cmd/internal/obj/ppc64: use mulli where possible
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This adds support to allow the use of mulli when one of the multiply
operands is a constant that fits in 16 bits.
This especially helps in the case where this instruction appears in
a loop since the load of the constant is not being moved out of the loop.
Some improvements seen in compress/flate on power9:
Decode/Digits/Huffman/1e4 259µs ± 0% 261µs ± 0% +0.57% (p=1.000 n=1+1)
Decode/Digits/Huffman/1e5 2.43ms ± 0% 2.45ms ± 0% +0.79% (p=1.000 n=1+1)
Decode/Digits/Huffman/1e6 23.9ms ± 0% 24.2ms ± 0% +0.86% (p=1.000 n=1+1)
Decode/Digits/Speed/1e4 278µs ± 0% 279µs ± 0% +0.34% (p=1.000 n=1+1)
Decode/Digits/Speed/1e5 2.80ms ± 0% 2.81ms ± 0% +0.29% (p=1.000 n=1+1)
Decode/Digits/Speed/1e6 28.0ms ± 0% 28.1ms ± 0% +0.28% (p=1.000 n=1+1)
Decode/Digits/Default/1e4 278µs ± 0% 278µs ± 0% +0.28% (p=1.000 n=1+1)
Decode/Digits/Default/1e5 2.68ms ± 0% 2.69ms ± 0% +0.19% (p=1.000 n=1+1)
Decode/Digits/Default/1e6 26.6ms ± 0% 26.6ms ± 0% +0.21% (p=1.000 n=1+1)
Decode/Digits/Compression/1e4 278µs ± 0% 278µs ± 0% +0.00% (p=1.000 n=1+1)
Decode/Digits/Compression/1e5 2.68ms ± 0% 2.69ms ± 0% +0.21% (p=1.000 n=1+1)
Decode/Digits/Compression/1e6 26.6ms ± 0% 26.6ms ± 0% +0.07% (p=1.000 n=1+1)
Decode/Newton/Huffman/1e4 322µs ± 0% 312µs ± 0% -2.84% (p=1.000 n=1+1)
Decode/Newton/Huffman/1e5 3.11ms ± 0% 2.91ms ± 0% -6.41% (p=1.000 n=1+1)
Decode/Newton/Huffman/1e6 31.4ms ± 0% 29.3ms ± 0% -6.85% (p=1.000 n=1+1)
Decode/Newton/Speed/1e4 282µs ± 0% 269µs ± 0% -4.69% (p=1.000 n=1+1)
Decode/Newton/Speed/1e5 2.29ms ± 0% 2.20ms ± 0% -4.13% (p=1.000 n=1+1)
Decode/Newton/Speed/1e6 22.7ms ± 0% 21.3ms ± 0% -6.06% (p=1.000 n=1+1)
Decode/Newton/Default/1e4 254µs ± 0% 237µs ± 0% -6.60% (p=1.000 n=1+1)
Decode/Newton/Default/1e5 1.86ms ± 0% 1.75ms ± 0% -5.99% (p=1.000 n=1+1)
Decode/Newton/Default/1e6 18.1ms ± 0% 17.4ms ± 0% -4.10% (p=1.000 n=1+1)
Decode/Newton/Compression/1e4 254µs ± 0% 244µs ± 0% -3.91% (p=1.000 n=1+1)
Decode/Newton/Compression/1e5 1.85ms ± 0% 1.79ms ± 0% -3.10% (p=1.000 n=1+1)
Decode/Newton/Compression/1e6 18.0ms ± 0% 17.3ms ± 0% -3.88% (p=1.000 n=1+1)
Change-Id: I840320fab1c4bf64c76b001c2651ab79f23df4eb
Reviewed-on: https://go-review.googlesource.com/c/go/+/259444
Run-TryBot: Lynn Boger
TryBot-Result: Go Bot
Reviewed-by: Paul Murphy
Reviewed-by: Carlos Eduardo Seo
Trust: Lynn Boger
---
src/cmd/asm/internal/asm/testdata/ppc64enc.s | 4 +++
src/cmd/compile/internal/gc/bench_test.go | 12 +++++++
src/cmd/compile/internal/ppc64/ssa.go | 3 +-
src/cmd/compile/internal/ssa/gen/PPC64.rules | 2 ++
src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 2 ++
src/cmd/compile/internal/ssa/opGen.go | 30 ++++++++++++++++
src/cmd/compile/internal/ssa/rewritePPC64.go | 54 ++++++++++++++++++++++++++++
src/cmd/internal/obj/ppc64/asm9.go | 9 ++---
8 files changed, 111 insertions(+), 5 deletions(-)
diff --git a/src/cmd/asm/internal/asm/testdata/ppc64enc.s b/src/cmd/asm/internal/asm/testdata/ppc64enc.s
index 869f8c2d4f..c6d7b59aad 100644
--- a/src/cmd/asm/internal/asm/testdata/ppc64enc.s
+++ b/src/cmd/asm/internal/asm/testdata/ppc64enc.s
@@ -204,12 +204,16 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MULLW R3, R4 // 7c8419d6
MULLW R3, R4, R5 // 7ca419d6
+ MULLW $10, R3 // 1c63000a
+ MULLW $10000000, R3 // 641f009863ff96807c7f19d6
MULLWCC R3, R4, R5 // 7ca419d7
MULHW R3, R4, R5 // 7ca41896
MULHWU R3, R4, R5 // 7ca41816
MULLD R3, R4 // 7c8419d2
MULLD R4, R4, R5 // 7ca421d2
+ MULLD $20, R4 // 1c840014
+ MULLD $200000000, R4 // 641f0beb63ffc2007c9f21d2
MULLDCC R3, R4, R5 // 7ca419d3
MULHD R3, R4, R5 // 7ca41892
MULHDCC R3, R4, R5 // 7ca41893
diff --git a/src/cmd/compile/internal/gc/bench_test.go b/src/cmd/compile/internal/gc/bench_test.go
index a2887f2f7b..8c4288128f 100644
--- a/src/cmd/compile/internal/gc/bench_test.go
+++ b/src/cmd/compile/internal/gc/bench_test.go
@@ -7,6 +7,7 @@ package gc
import "testing"
var globl int64
+var globl32 int32
func BenchmarkLoadAdd(b *testing.B) {
x := make([]int64, 1024)
@@ -42,6 +43,17 @@ func BenchmarkModify(b *testing.B) {
}
}
+func BenchmarkMullImm(b *testing.B) {
+ x := make([]int32, 1024)
+ for i := 0; i < b.N; i++ {
+ var s int32
+ for i := range x {
+ s += x[i] * 100
+ }
+ globl32 = s
+ }
+}
+
func BenchmarkConstModify(b *testing.B) {
a := make([]int64, 1024)
for i := 0; i < b.N; i++ {
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index d83b2df379..1ece4d999f 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -677,7 +677,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[0].Reg()
case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst,
- ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst, ssa.OpPPC64EXTSWSLconst:
+ ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst,
+ ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst, ssa.OpPPC64EXTSWSLconst, ssa.OpPPC64MULLWconst, ssa.OpPPC64MULLDconst:
p := s.Prog(v.Op.Asm())
p.Reg = v.Args[0].Reg()
p.From.Type = obj.TYPE_CONST
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index 83ee4c499b..a05cfee654 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -821,6 +821,8 @@
(ADDconst [c] (MOVDaddr [d] {sym} x)) && is32Bit(c+int64(d)) => (MOVDaddr [int32(c+int64(d))] {sym} x)
+(MULL(W|D) x (MOVDconst [c])) && is16Bit(c) => (MULL(W|D)const [int32(c)] x)
+
// Subtract from (with carry, but ignored) constant.
// Note, these clobber the carry bit.
(SUB (MOVDconst [c]) x) && is32Bit(c) => (SUBFCconst [c] x)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
index 28317928a8..5885660597 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -181,6 +181,8 @@ func init() {
{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true}, // arg0*arg1 (signed 64-bit)
{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true}, // arg0*arg1 (signed 32-bit)
+ {name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int32", typ: "Int64"}, // arg0*auxInt (signed 64-bit)
+ {name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int64"}, // arg0*auxInt (signed 64-bit)
{name: "MADDLD", argLength: 3, reg: gp31, asm: "MADDLD", typ: "Int64"}, // (arg0*arg1)+arg2 (signed 64-bit)
{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", commutative: true}, // (arg0 * arg1) >> 64, signed
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index d7d2b24a48..051550fb17 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -1832,6 +1832,8 @@ const (
OpPPC64FSUBS
OpPPC64MULLD
OpPPC64MULLW
+ OpPPC64MULLDconst
+ OpPPC64MULLWconst
OpPPC64MADDLD
OpPPC64MULHD
OpPPC64MULHW
@@ -24377,6 +24379,34 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "MULLDconst",
+ auxType: auxInt32,
+ argLen: 1,
+ asm: ppc64.AMULLD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+ },
+ outputs: []outputInfo{
+ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+ },
+ },
+ },
+ {
+ name: "MULLWconst",
+ auxType: auxInt32,
+ argLen: 1,
+ asm: ppc64.AMULLW,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+ },
+ outputs: []outputInfo{
+ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+ },
+ },
+ },
{
name: "MADDLD",
argLen: 3,
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 9822637b05..1b8a5a78ca 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -568,6 +568,10 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpPPC64MOVWstorezero(v)
case OpPPC64MTVSRD:
return rewriteValuePPC64_OpPPC64MTVSRD(v)
+ case OpPPC64MULLD:
+ return rewriteValuePPC64_OpPPC64MULLD(v)
+ case OpPPC64MULLW:
+ return rewriteValuePPC64_OpPPC64MULLW(v)
case OpPPC64NEG:
return rewriteValuePPC64_OpPPC64NEG(v)
case OpPPC64NOR:
@@ -11003,6 +11007,56 @@ func rewriteValuePPC64_OpPPC64MTVSRD(v *Value) bool {
}
return false
}
+func rewriteValuePPC64_OpPPC64MULLD(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (MULLD x (MOVDconst [c]))
+ // cond: is16Bit(c)
+ // result: (MULLDconst [int32(c)] x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpPPC64MOVDconst {
+ continue
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ if !(is16Bit(c)) {
+ continue
+ }
+ v.reset(OpPPC64MULLDconst)
+ v.AuxInt = int32ToAuxInt(int32(c))
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64MULLW(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (MULLW x (MOVDconst [c]))
+ // cond: is16Bit(c)
+ // result: (MULLWconst [int32(c)] x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpPPC64MOVDconst {
+ continue
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ if !(is16Bit(c)) {
+ continue
+ }
+ v.reset(OpPPC64MULLWconst)
+ v.AuxInt = int32ToAuxInt(int32(c))
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
+ return false
+}
func rewriteValuePPC64_OpPPC64NEG(v *Value) bool {
v_0 := v.Args[0]
// match: (NEG (ADDconst [c] x))
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 928e299f43..c2e8e9e9d0 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -1279,6 +1279,9 @@ func buildop(ctxt *obj.Link) {
case AREMD:
opset(AREMDU, r0)
+ case AMULLW:
+ opset(AMULLD, r0)
+
case ADIVW: /* op Rb[,Ra],Rd */
opset(AMULHW, r0)
@@ -1312,7 +1315,6 @@ func buildop(ctxt *obj.Link) {
opset(AMULHDCC, r0)
opset(AMULHDU, r0)
opset(AMULHDUCC, r0)
- opset(AMULLD, r0)
opset(AMULLDCC, r0)
opset(AMULLDVCC, r0)
opset(AMULLDV, r0)
@@ -1996,7 +1998,6 @@ func buildop(ctxt *obj.Link) {
AMOVB, /* macro: move byte with sign extension */
AMOVBU, /* macro: move byte with sign extension & update */
AMOVFL,
- AMULLW,
/* op $s[,r2],r3; op r1[,r2],r3; no cc/v */
ASUBC, /* op r1,$s,r3; op r1[,r2],r3 */
ASTSW,
@@ -4990,8 +4991,8 @@ func (c *ctxt9) opirr(a obj.As) uint32 {
case ADARN:
return OPVCC(31, 755, 0, 0) /* darn - v3.00 */
- case AMULLW:
- return OPVCC(7, 0, 0, 0)
+ case AMULLW, AMULLD:
+ return OPVCC(7, 0, 0, 0) /* mulli works with MULLW or MULLD */
case AOR:
return OPVCC(24, 0, 0, 0)
--
cgit v1.2.3-54-g00ecf
From 04c7e32517faf6257986e7c4cdd3f5f03eeae37b Mon Sep 17 00:00:00 2001
From: Dan Scales
Date: Wed, 30 Sep 2020 16:34:47 -0700
Subject: compress/flate: remove unneeded zeroing of bytes array in
(*huffmanBitWriter).reset
There is no correctness reason to zero out the w.bytes array in (w
*huffmanBitWriter).reset, since w.nbytes is correctly set to zero. The elements of
the bytes array are always written sequentially, with nbytes indicating how many
elements have been written, and are only read up to the current value of nybytes.
We have a pprof profile of a web server that compresses its request/responses, and
the zeroing in reset() is taking up 2.6% of the CPU time of the server (and could
be causing more slowdowns elsewhere due to its effects on the cache). This
overhead may be showing up especially because there are many request/responses
that are all fairly small.
I'm not sure if the zeroing of the bytes array was intended as extra protection of
data across reset uses in the same program, but no protection is needed as long as
the huffman_bit_writer code remains correct.
Change-Id: I67f2b2f56cff9dcc38d8fc0aea885bb010aeedbf
Reviewed-on: https://go-review.googlesource.com/c/go/+/258577
Run-TryBot: Dan Scales
Run-TryBot: Joe Tsai
TryBot-Result: Go Bot
Reviewed-by: Klaus Post
Reviewed-by: Joe Tsai
Trust: Joe Tsai
Trust: Dan Scales
---
src/compress/flate/huffman_bit_writer.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go
index f111f9f592..b3ae76d082 100644
--- a/src/compress/flate/huffman_bit_writer.go
+++ b/src/compress/flate/huffman_bit_writer.go
@@ -75,7 +75,8 @@ type huffmanBitWriter struct {
writer io.Writer
// Data waiting to be written is bytes[0:nbytes]
- // and then the low nbits of bits.
+ // and then the low nbits of bits. Data is always written
+ // sequentially into the bytes array.
bits uint64
nbits uint
bytes [bufferSize]byte
@@ -105,7 +106,6 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
func (w *huffmanBitWriter) reset(writer io.Writer) {
w.writer = writer
w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil
- w.bytes = [bufferSize]byte{}
}
func (w *huffmanBitWriter) flush() {
--
cgit v1.2.3-54-g00ecf
From 28e549dec3954b36d0c83442be913d8709d7e5ae Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Sat, 12 Sep 2020 12:33:24 -0400
Subject: runtime: use sigaltstack on macOS/ARM64
Currently we don't use sigaltstack on darwin/arm64, as is not
supported on iOS. However, it is supported on macOS. Use it.
(iOS remains unchanged.)
Change-Id: Icc154c5e2edf2dbdc8ca68741ad9157fc15a72ee
Reviewed-on: https://go-review.googlesource.com/c/go/+/256917
Trust: Cherry Zhang
Reviewed-by: Ian Lance Taylor
---
misc/cgo/test/sigaltstack.go | 2 +-
src/cmd/internal/obj/arm64/obj7.go | 2 +-
src/runtime/mkpreempt.go | 7 ++-----
src/runtime/os_darwin.go | 8 ++++----
src/runtime/preempt_arm64.s | 3 ---
src/runtime/stack.go | 2 +-
src/runtime/sys_darwin_arm64.s | 22 ++++++++++++++++++----
7 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/misc/cgo/test/sigaltstack.go b/misc/cgo/test/sigaltstack.go
index 27b753a147..034cc4b371 100644
--- a/misc/cgo/test/sigaltstack.go
+++ b/misc/cgo/test/sigaltstack.go
@@ -62,7 +62,7 @@ import (
func testSigaltstack(t *testing.T) {
switch {
- case runtime.GOOS == "solaris", runtime.GOOS == "illumos", (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64":
+ case runtime.GOOS == "solaris", runtime.GOOS == "illumos", runtime.GOOS == "ios" && runtime.GOARCH == "arm64":
t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index 56da854f16..f1bc2583cb 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -589,7 +589,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q1.To.Reg = REGSP
q1.Spadj = c.autosize
- if c.ctxt.Headtype == objabi.Hdarwin {
+ if objabi.GOOS == "ios" {
// iOS does not support SA_ONSTACK. We will run the signal handler
// on the G stack. If we write below SP, it may be clobbered by
// the signal handler. So we save LR after decrementing SP.
diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go
index c5bfb0f207..40683bb9d9 100644
--- a/src/runtime/mkpreempt.go
+++ b/src/runtime/mkpreempt.go
@@ -340,12 +340,9 @@ func genARM64() {
p("MOVD R29, -8(RSP)") // save frame pointer (only used on Linux)
p("SUB $8, RSP, R29") // set up new frame pointer
p("#endif")
- // On darwin, save the LR again after decrementing SP. We run the
- // signal handler on the G stack (as it doesn't support SA_ONSTACK),
+ // On iOS, save the LR again after decrementing SP. We run the
+ // signal handler on the G stack (as it doesn't support sigaltstack),
// so any writes below SP may be clobbered.
- p("#ifdef GOOS_darwin")
- p("MOVD R30, (RSP)")
- p("#endif")
p("#ifdef GOOS_ios")
p("MOVD R30, (RSP)")
p("#endif")
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
index 01c40b4813..394bd6fb0f 100644
--- a/src/runtime/os_darwin.go
+++ b/src/runtime/os_darwin.go
@@ -289,9 +289,9 @@ func mpreinit(mp *m) {
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, cannot allocate memory.
func minit() {
- // The alternate signal stack is buggy on arm64.
+ // iOS does not support alternate signal stack.
// The signal handler handles it directly.
- if GOARCH != "arm64" {
+ if !(GOOS == "ios" && GOARCH == "arm64") {
minitSignalStack()
}
minitSignalMask()
@@ -301,9 +301,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
//go:nosplit
func unminit() {
- // The alternate signal stack is buggy on arm64.
+ // iOS does not support alternate signal stack.
// See minit.
- if GOARCH != "arm64" {
+ if !(GOOS == "ios" && GOARCH == "arm64") {
unminitSignals()
}
}
diff --git a/src/runtime/preempt_arm64.s b/src/runtime/preempt_arm64.s
index d0e77659c3..36ee13282c 100644
--- a/src/runtime/preempt_arm64.s
+++ b/src/runtime/preempt_arm64.s
@@ -10,9 +10,6 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
MOVD R29, -8(RSP)
SUB $8, RSP, R29
#endif
- #ifdef GOOS_darwin
- MOVD R30, (RSP)
- #endif
#ifdef GOOS_ios
MOVD R30, (RSP)
#endif
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 3802cd049e..2afc2635aa 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -66,7 +66,7 @@ const (
// to each stack below the usual guard area for OS-specific
// purposes like signal handling. Used on Windows, Plan 9,
// and iOS because they do not use a separate stack.
- _StackSystem = sys.GoosWindows*512*sys.PtrSize + sys.GoosPlan9*512 + (sys.GoosDarwin+sys.GoosIos)*sys.GoarchArm64*1024
+ _StackSystem = sys.GoosWindows*512*sys.PtrSize + sys.GoosPlan9*512 + sys.GoosIos*sys.GoarchArm64*1024
// The minimum size of stack used by Go code
_StackMin = 2048
diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s
index 585d4f2c64..427cb17781 100644
--- a/src/runtime/sys_darwin_arm64.s
+++ b/src/runtime/sys_darwin_arm64.s
@@ -202,6 +202,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192
BEQ 2(PC)
BL runtime·load_g(SB)
+#ifdef GOOS_ios
MOVD RSP, R6
CMP $0, g
BEQ nog
@@ -226,16 +227,21 @@ nog:
// Switch to gsignal stack.
MOVD R6, RSP
- // Call sigtrampgo.
+ // Save arguments.
MOVW R0, (8*1)(RSP)
MOVD R1, (8*2)(RSP)
MOVD R2, (8*3)(RSP)
+#endif
+
+ // Call sigtrampgo.
MOVD $runtime·sigtrampgo(SB), R11
BL (R11)
+#ifdef GOOS_ios
// Switch to old stack.
MOVD (8*4)(RSP), R5
MOVD R5, RSP
+#endif
// Restore callee-save registers.
MOVD (8*4)(RSP), R19
@@ -329,12 +335,20 @@ TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0
ADD $16, RSP
RET
-// sigaltstack on iOS is not supported and will always
-// run the signal handler on the main stack, so our sigtramp has
-// to do the stack switch ourselves.
TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0
+#ifdef GOOS_ios
+ // sigaltstack on iOS is not supported and will always
+ // run the signal handler on the main stack, so our sigtramp has
+ // to do the stack switch ourselves.
MOVW $43, R0
BL libc_exit(SB)
+#else
+ MOVD 8(R0), R1 // arg 2 old
+ MOVD 0(R0), R0 // arg 1 new
+ CALL libc_sigaltstack(SB)
+ CBZ R0, 2(PC)
+ BL notok<>(SB)
+#endif
RET
// Thread related functions
--
cgit v1.2.3-54-g00ecf
From a739306ca7d9ea3a98acca59b853fe889f04c28c Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 17 Sep 2020 10:53:10 -0400
Subject: runtime: enable more address bits on macOS/ARM64
Apparently macOS/ARM64 has 47-bit addresses, instead of 33-bit as
on ios/ARM64. Enable more address bits.
Updates #38485.
Change-Id: I8aa64ba22a3933e3d9c4fffd17d902b5f31c30e3
Reviewed-on: https://go-review.googlesource.com/c/go/+/256918
Trust: Cherry Zhang
Reviewed-by: Ian Lance Taylor
Reviewed-by: Michael Knyszek
---
src/runtime/malloc.go | 8 ++++----
src/runtime/mpagealloc_32bit.go | 4 ++--
src/runtime/mpagealloc_64bit.go | 4 ++--
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index c71f856f09..f7e9b7c4b4 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -198,7 +198,7 @@ const (
// mips32 only has access to the low 2GB of virtual memory, so
// we further limit it to 31 bits.
//
- // On darwin/arm64, although 64-bit pointers are presumably
+ // On ios/arm64, although 64-bit pointers are presumably
// available, pointers are truncated to 33 bits. Furthermore,
// only the top 4 GiB of the address space are actually available
// to the application, but we allow the whole 33 bits anyway for
@@ -207,7 +207,7 @@ const (
// arenaBaseOffset to offset into the top 4 GiB.
//
// WebAssembly currently has a limit of 4GB linear memory.
- heapAddrBits = (_64bit*(1-sys.GoarchWasm)*(1-(sys.GoosDarwin+sys.GoosIos)*sys.GoarchArm64))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle)) + 33*(sys.GoosDarwin+sys.GoosIos)*sys.GoarchArm64
+ heapAddrBits = (_64bit*(1-sys.GoarchWasm)*(1-sys.GoosIos*sys.GoarchArm64))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle)) + 33*sys.GoosIos*sys.GoarchArm64
// maxAlloc is the maximum size of an allocation. On 64-bit,
// it's theoretically possible to allocate 1<= 0; i-- {
var p uintptr
switch {
- case GOARCH == "arm64" && (GOOS == "darwin" || GOOS == "ios"):
+ case GOARCH == "arm64" && GOOS == "ios":
p = uintptr(i)<<40 | uintptrMask&(0x0013<<28)
case GOARCH == "arm64":
p = uintptr(i)<<40 | uintptrMask&(0x0040<<32)
diff --git a/src/runtime/mpagealloc_32bit.go b/src/runtime/mpagealloc_32bit.go
index 6658a900ac..90f1e54d6c 100644
--- a/src/runtime/mpagealloc_32bit.go
+++ b/src/runtime/mpagealloc_32bit.go
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build 386 arm mips mipsle wasm darwin,arm64
+// +build 386 arm mips mipsle wasm ios,arm64
// wasm is a treated as a 32-bit architecture for the purposes of the page
// allocator, even though it has 64-bit pointers. This is because any wasm
// pointer always has its top 32 bits as zero, so the effective heap address
// space is only 2^32 bytes in size (see heapAddrBits).
-// darwin/arm64 is treated as a 32-bit architecture for the purposes of the
+// ios/arm64 is treated as a 32-bit architecture for the purposes of the
// page allocator, even though it has 64-bit pointers and a 33-bit address
// space (see heapAddrBits). The 33 bit address space cannot be rounded up
// to 64 bits because there are too many summary levels to fit in just 33
diff --git a/src/runtime/mpagealloc_64bit.go b/src/runtime/mpagealloc_64bit.go
index 831626e4b2..a1691ba802 100644
--- a/src/runtime/mpagealloc_64bit.go
+++ b/src/runtime/mpagealloc_64bit.go
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 !darwin,arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x
+// +build amd64 !ios,arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x
-// See mpagealloc_32bit.go for why darwin/arm64 is excluded here.
+// See mpagealloc_32bit.go for why ios/arm64 is excluded here.
package runtime
--
cgit v1.2.3-54-g00ecf
From 2e4ceaf963fc2a0ce95a198769012e62ec4e28ae Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 17 Sep 2020 12:39:43 -0400
Subject: cmd/dist: enable more tests on macOS/ARM64
Unlike iOS, macOS ARM64 is more of a fully featured OS. Enable
more tests.
Updates #38485.
Change-Id: I2e2240c848d21996db2b950a4a6856987f7a652c
Reviewed-on: https://go-review.googlesource.com/c/go/+/256919
Trust: Cherry Zhang
Reviewed-by: Ian Lance Taylor
---
src/cmd/dist/test.go | 2 +-
test/fixedbugs/bug429_run.go | 7 ++++++-
test/fixedbugs/issue21576.go | 7 ++++++-
test/nilptr.go | 3 ++-
4 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index da894e3eef..abe496fdee 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -903,7 +903,7 @@ func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec.
}
func (t *tester) iOS() bool {
- return (goos == "darwin" || goos == "ios") && goarch == "arm64"
+ return goos == "ios"
}
func (t *tester) out(v string) {
diff --git a/test/fixedbugs/bug429_run.go b/test/fixedbugs/bug429_run.go
index c6a02aae5e..60cc5b62de 100644
--- a/test/fixedbugs/bug429_run.go
+++ b/test/fixedbugs/bug429_run.go
@@ -1,6 +1,11 @@
-// +build !nacl,!js
// run
+// +build !nacl,!js
+// +build !darwin !arm64
+
+// Skip on darwin/arm64 as it requires external linking, which brings in
+// cgo, causing deadlock detection not working.
+
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/test/fixedbugs/issue21576.go b/test/fixedbugs/issue21576.go
index b7a32f07ac..3797a8c9ba 100644
--- a/test/fixedbugs/issue21576.go
+++ b/test/fixedbugs/issue21576.go
@@ -1,6 +1,11 @@
-// +build !nacl,!js
// run
+// +build !nacl,!js
+// +build !darwin !arm64
+
+// Skip on darwin/arm64 as it requires external linking, which brings in
+// cgo, causing deadlock detection not working.
+
// 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.
diff --git a/test/nilptr.go b/test/nilptr.go
index 90f57c54b6..c9a044dd36 100644
--- a/test/nilptr.go
+++ b/test/nilptr.go
@@ -8,7 +8,8 @@
// in a large address space.
// +build !aix
-// Address space starts at 1<<32 on AIX, so dummy is too far.
+// +build !darwin !arm64
+// Address space starts at 1<<32 on AIX and on darwin/arm64, so dummy is too far.
package main
--
cgit v1.2.3-54-g00ecf
From db428ad7b61ed757671162054252b4326045e96c Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 17 Sep 2020 15:02:26 -0400
Subject: all: enable more tests on macOS/ARM64
Updates #38485.
Change-Id: Iac96f5ffe88521fcb11eab306d0df6463bdce046
Reviewed-on: https://go-review.googlesource.com/c/go/+/256920
Trust: Cherry Zhang
Reviewed-by: Dmitri Shuralyov
Reviewed-by: Ian Lance Taylor
---
misc/cgo/testcarchive/carchive_test.go | 2 +-
src/cmd/doc/doc_test.go | 2 +-
src/cmd/go/internal/work/build_test.go | 6 ++----
src/go/build/build_test.go | 4 ++--
src/log/syslog/syslog_test.go | 7 +------
src/net/dial_test.go | 2 +-
src/net/platform_test.go | 2 +-
src/path/filepath/path_test.go | 4 ++--
src/runtime/debug/panic_test.go | 4 ++--
src/syscall/syscall_unix_test.go | 2 +-
10 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go
index 2e223ea369..6ed25d8948 100644
--- a/misc/cgo/testcarchive/carchive_test.go
+++ b/misc/cgo/testcarchive/carchive_test.go
@@ -603,7 +603,7 @@ func TestExtar(t *testing.T) {
if runtime.Compiler == "gccgo" {
t.Skip("skipping -extar test when using gccgo")
}
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skip("shell scripts are not executable on iOS hosts")
}
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go
index 47602833d3..39530e3c2d 100644
--- a/src/cmd/doc/doc_test.go
+++ b/src/cmd/doc/doc_test.go
@@ -36,7 +36,7 @@ func TestMain(m *testing.M) {
}
func maybeSkip(t *testing.T) {
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skip("iOS does not have a full file tree")
}
}
diff --git a/src/cmd/go/internal/work/build_test.go b/src/cmd/go/internal/work/build_test.go
index afed0fba72..904aee0684 100644
--- a/src/cmd/go/internal/work/build_test.go
+++ b/src/cmd/go/internal/work/build_test.go
@@ -221,10 +221,8 @@ func pkgImportPath(pkgpath string) *load.Package {
// See https://golang.org/issue/18878.
func TestRespectSetgidDir(t *testing.T) {
switch runtime.GOOS {
- case "darwin", "ios":
- if runtime.GOARCH == "arm64" {
- t.Skip("can't set SetGID bit with chmod on iOS")
- }
+ case "ios":
+ t.Skip("can't set SetGID bit with chmod on iOS")
case "windows", "plan9":
t.Skip("chown/chmod setgid are not supported on Windows or Plan 9")
}
diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go
index 22c62ce87d..2f2e80b5a8 100644
--- a/src/go/build/build_test.go
+++ b/src/go/build/build_test.go
@@ -120,7 +120,7 @@ func TestMultiplePackageImport(t *testing.T) {
}
func TestLocalDirectory(t *testing.T) {
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
}
@@ -250,7 +250,7 @@ func TestMatchFile(t *testing.T) {
}
func TestImportCmd(t *testing.T) {
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
}
diff --git a/src/log/syslog/syslog_test.go b/src/log/syslog/syslog_test.go
index dd2f83e04f..30abfae550 100644
--- a/src/log/syslog/syslog_test.go
+++ b/src/log/syslog/syslog_test.go
@@ -51,12 +51,7 @@ func testableNetwork(network string) bool {
switch network {
case "unix", "unixgram":
switch runtime.GOOS {
- case "darwin", "ios":
- switch runtime.GOARCH {
- case "arm64":
- return false
- }
- case "android":
+ case "ios", "android":
return false
}
}
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 2706de4442..57cf5554ad 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -990,7 +990,7 @@ func TestDialerControl(t *testing.T) {
// except that it won't skip testing on non-mobile builders.
func mustHaveExternalNetwork(t *testing.T) {
t.Helper()
- mobile := runtime.GOOS == "android" || (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64"
+ mobile := runtime.GOOS == "android" || runtime.GOOS == "ios"
if testenv.Builder() == "" || mobile {
testenv.MustHaveExternalNetwork(t)
}
diff --git a/src/net/platform_test.go b/src/net/platform_test.go
index 4b92bb6df0..2da23dedce 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -82,7 +82,7 @@ func testableNetwork(network string) bool {
}
func iOS() bool {
- return (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64"
+ return runtime.GOOS == "ios"
}
// testableAddress reports whether address of network is testable on
diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index ca100ff071..6a8700e413 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -431,7 +431,7 @@ func chtmpdir(t *testing.T) (restore func()) {
}
func TestWalk(t *testing.T) {
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
restore := chtmpdir(t)
defer restore()
}
@@ -1278,7 +1278,7 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
}
func TestBug3486(t *testing.T) { // https://golang.org/issue/3486
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
}
root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test")
diff --git a/src/runtime/debug/panic_test.go b/src/runtime/debug/panic_test.go
index 93be216985..b67a3de4f9 100644
--- a/src/runtime/debug/panic_test.go
+++ b/src/runtime/debug/panic_test.go
@@ -20,8 +20,8 @@ func TestPanicOnFault(t *testing.T) {
if runtime.GOARCH == "s390x" {
t.Skip("s390x fault addresses are missing the low order bits")
}
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
- t.Skip("darwin/arm64 doesn't provide fault addresses")
+ if runtime.GOOS == "ios" {
+ t.Skip("iOS doesn't provide fault addresses")
}
m, err := syscall.Mmap(-1, 0, 0x1000, syscall.PROT_READ /* Note: no PROT_WRITE */, syscall.MAP_SHARED|syscall.MAP_ANON)
if err != nil {
diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go
index 7e9bb0c3ac..d754c075f1 100644
--- a/src/syscall/syscall_unix_test.go
+++ b/src/syscall/syscall_unix_test.go
@@ -70,7 +70,7 @@ func _() {
// Thus this test also verifies that the Flock_t structure can be
// roundtripped with F_SETLK and F_GETLK.
func TestFcntlFlock(t *testing.T) {
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skip("skipping; no child processes allowed on iOS")
}
flock := syscall.Flock_t{
--
cgit v1.2.3-54-g00ecf
From 930fa890c9b6a75700bda3dc4043de81350749ea Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick
Date: Tue, 6 Oct 2020 10:53:11 -0700
Subject: net/http: add Transport.GetProxyConnectHeader
Fixes golang/go#41048
Change-Id: I38e01605bffb6f85100c098051b0c416dd77f261
Reviewed-on: https://go-review.googlesource.com/c/go/+/259917
Trust: Brad Fitzpatrick
Run-TryBot: Brad Fitzpatrick
TryBot-Result: Go Bot
Reviewed-by: Damien Neil
---
src/net/http/transport.go | 23 ++++++++++++++++++-
src/net/http/transport_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index b97c4268b5..4546166430 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -240,8 +240,18 @@ type Transport struct {
// ProxyConnectHeader optionally specifies headers to send to
// proxies during CONNECT requests.
+ // To set the header dynamically, see GetProxyConnectHeader.
ProxyConnectHeader Header
+ // GetProxyConnectHeader optionally specifies a func to return
+ // headers to send to proxyURL during a CONNECT request to the
+ // ip:port target.
+ // If it returns an error, the Transport's RoundTrip fails with
+ // that error. It can return (nil, nil) to not add headers.
+ // If GetProxyConnectHeader is non-nil, ProxyConnectHeader is
+ // ignored.
+ GetProxyConnectHeader func(ctx context.Context, proxyURL *url.URL, target string) (Header, error)
+
// MaxResponseHeaderBytes specifies a limit on how many
// response bytes are allowed in the server's response
// header.
@@ -313,6 +323,7 @@ func (t *Transport) Clone() *Transport {
ResponseHeaderTimeout: t.ResponseHeaderTimeout,
ExpectContinueTimeout: t.ExpectContinueTimeout,
ProxyConnectHeader: t.ProxyConnectHeader.Clone(),
+ GetProxyConnectHeader: t.GetProxyConnectHeader,
MaxResponseHeaderBytes: t.MaxResponseHeaderBytes,
ForceAttemptHTTP2: t.ForceAttemptHTTP2,
WriteBufferSize: t.WriteBufferSize,
@@ -1623,7 +1634,17 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *pers
}
case cm.targetScheme == "https":
conn := pconn.conn
- hdr := t.ProxyConnectHeader
+ var hdr Header
+ if t.GetProxyConnectHeader != nil {
+ var err error
+ hdr, err = t.GetProxyConnectHeader(ctx, cm.proxyURL, cm.targetAddr)
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+ } else {
+ hdr = t.ProxyConnectHeader
+ }
if hdr == nil {
hdr = make(Header)
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index f4b7623630..a1c9e822b4 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -5174,6 +5174,57 @@ func TestTransportProxyConnectHeader(t *testing.T) {
}
}
+func TestTransportProxyGetConnectHeader(t *testing.T) {
+ defer afterTest(t)
+ reqc := make(chan *Request, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.Method != "CONNECT" {
+ t.Errorf("method = %q; want CONNECT", r.Method)
+ }
+ reqc <- r
+ c, _, err := w.(Hijacker).Hijack()
+ if err != nil {
+ t.Errorf("Hijack: %v", err)
+ return
+ }
+ c.Close()
+ }))
+ defer ts.Close()
+
+ c := ts.Client()
+ c.Transport.(*Transport).Proxy = func(r *Request) (*url.URL, error) {
+ return url.Parse(ts.URL)
+ }
+ // These should be ignored:
+ c.Transport.(*Transport).ProxyConnectHeader = Header{
+ "User-Agent": {"foo"},
+ "Other": {"bar"},
+ }
+ c.Transport.(*Transport).GetProxyConnectHeader = func(ctx context.Context, proxyURL *url.URL, target string) (Header, error) {
+ return Header{
+ "User-Agent": {"foo2"},
+ "Other": {"bar2"},
+ }, nil
+ }
+
+ res, err := c.Get("https://dummy.tld/") // https to force a CONNECT
+ if err == nil {
+ res.Body.Close()
+ t.Errorf("unexpected success")
+ }
+ select {
+ case <-time.After(3 * time.Second):
+ t.Fatal("timeout")
+ case r := <-reqc:
+ if got, want := r.Header.Get("User-Agent"), "foo2"; got != want {
+ t.Errorf("CONNECT request User-Agent = %q; want %q", got, want)
+ }
+ if got, want := r.Header.Get("Other"), "bar2"; got != want {
+ t.Errorf("CONNECT request Other = %q; want %q", got, want)
+ }
+ }
+}
+
var errFakeRoundTrip = errors.New("fake roundtrip")
type funcRoundTripper func()
@@ -5842,6 +5893,7 @@ func TestTransportClone(t *testing.T) {
ResponseHeaderTimeout: time.Second,
ExpectContinueTimeout: time.Second,
ProxyConnectHeader: Header{},
+ GetProxyConnectHeader: func(context.Context, *url.URL, string) (Header, error) { return nil, nil },
MaxResponseHeaderBytes: 1,
ForceAttemptHTTP2: true,
TLSNextProto: map[string]func(authority string, c *tls.Conn) RoundTripper{
--
cgit v1.2.3-54-g00ecf
From 04b8a9fea57e37589d82410281f22ebde0027808 Mon Sep 17 00:00:00 2001
From: Keith Randall
Date: Tue, 6 Oct 2020 14:42:15 -0700
Subject: all: implement GO386=softfloat
Backstop support for non-sse2 chips now that 387 is gone.
RELNOTE=yes
Change-Id: Ib10e69c4a3654c15a03568f93393437e1939e013
Reviewed-on: https://go-review.googlesource.com/c/go/+/260017
Trust: Keith Randall
Run-TryBot: Keith Randall
TryBot-Result: Go Bot
Reviewed-by: Ian Lance Taylor
---
src/cmd/compile/internal/x86/galign.go | 15 +++++++++++++++
src/cmd/dist/build.go | 11 +++++++++++
src/cmd/dist/buildruntime.go | 2 ++
src/cmd/go/alldocs.go | 3 +++
src/cmd/go/internal/cfg/cfg.go | 3 +++
src/cmd/go/internal/help/helpdoc.go | 3 +++
src/cmd/internal/objabi/util.go | 9 +--------
src/internal/cfg/cfg.go | 1 +
test/codegen/arithmetic.go | 6 +++---
test/codegen/floats.go | 8 ++++----
test/codegen/math.go | 2 +-
test/codegen/memops.go | 32 ++++++++++++++++----------------
test/run.go | 12 ++++++------
13 files changed, 69 insertions(+), 38 deletions(-)
diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go
index 2d20b6a6d0..e137daa3fc 100644
--- a/src/cmd/compile/internal/x86/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -7,6 +7,9 @@ package x86
import (
"cmd/compile/internal/gc"
"cmd/internal/obj/x86"
+ "cmd/internal/objabi"
+ "fmt"
+ "os"
)
func Init(arch *gc.Arch) {
@@ -15,6 +18,18 @@ func Init(arch *gc.Arch) {
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
arch.MAXWIDTH = (1 << 32) - 1
+ switch v := objabi.GO386; v {
+ case "sse2":
+ case "softfloat":
+ arch.SoftFloat = true
+ case "387":
+ fmt.Fprintf(os.Stderr, "unsupported setting GO386=387. Consider using GO386=softfloat instead.\n")
+ gc.Exit(1)
+ default:
+ fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v)
+ gc.Exit(1)
+
+ }
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 3b3eb113b1..69a66abd2d 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -30,6 +30,7 @@ var (
gohostos string
goos string
goarm string
+ go386 string
gomips string
gomips64 string
goppc64 string
@@ -141,6 +142,12 @@ func xinit() {
}
goarm = b
+ b = os.Getenv("GO386")
+ if b == "" {
+ b = "sse2"
+ }
+ go386 = b
+
b = os.Getenv("GOMIPS")
if b == "" {
b = "hardfloat"
@@ -212,6 +219,7 @@ func xinit() {
defaultldso = os.Getenv("GO_LDSO")
// For tools being invoked but also for os.ExpandEnv.
+ os.Setenv("GO386", go386)
os.Setenv("GOARCH", goarch)
os.Setenv("GOARM", goarm)
os.Setenv("GOHOSTARCH", gohostarch)
@@ -1153,6 +1161,9 @@ func cmdenv() {
if goarch == "arm" {
xprintf(format, "GOARM", goarm)
}
+ if goarch == "386" {
+ xprintf(format, "GO386", go386)
+ }
if goarch == "mips" || goarch == "mipsle" {
xprintf(format, "GOMIPS", gomips)
}
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index 67d1d72db4..2744951597 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -41,6 +41,7 @@ func mkzversion(dir, file string) {
// package objabi
//
// const defaultGOROOT =
+// const defaultGO386 =
// const defaultGOARM =
// const defaultGOMIPS =
// const defaultGOMIPS64 =
@@ -69,6 +70,7 @@ func mkzbootstrap(file string) {
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf)
+ fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 14840efb22..5cb32c80e9 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -1852,6 +1852,9 @@
// GOARM
// For GOARCH=arm, the ARM architecture for which to compile.
// Valid values are 5, 6, 7.
+// GO386
+// For GOARCH=386, how to implement floating point instructions.
+// Valid values are sse2 (default), softfloat.
// GOMIPS
// For GOARCH=mips{,le}, whether to use floating point instructions.
// Valid values are hardfloat (default), softfloat.
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index 9169c12d8f..67d581f6e6 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -256,6 +256,7 @@ var (
// Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
+ GO386 = envOr("GO386", objabi.GO386)
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
@@ -279,6 +280,8 @@ func GetArchEnv() (key, val string) {
switch Goarch {
case "arm":
return "GOARM", GOARM
+ case "386":
+ return "GO386", GO386
case "mips", "mipsle":
return "GOMIPS", GOMIPS
case "mips64", "mips64le":
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index befa10a0e4..8dfabbaa4a 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -581,6 +581,9 @@ Architecture-specific environment variables:
GOARM
For GOARCH=arm, the ARM architecture for which to compile.
Valid values are 5, 6, 7.
+ GO386
+ For GOARCH=386, how to implement floating point instructions.
+ Valid values are sse2 (default), softfloat.
GOMIPS
For GOARCH=mips{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go
index cedb2d0a26..b81b73a022 100644
--- a/src/cmd/internal/objabi/util.go
+++ b/src/cmd/internal/objabi/util.go
@@ -24,6 +24,7 @@ var (
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", defaultGOARCH)
GOOS = envOr("GOOS", defaultGOOS)
+ GO386 = envOr("GO386", defaultGO386)
GOAMD64 = goamd64()
GOARM = goarm()
GOMIPS = gomips()
@@ -135,14 +136,6 @@ func init() {
if GOARCH != "amd64" {
Regabi_enabled = 0
}
-
- if v := os.Getenv("GO386"); v != "" && v != "sse2" {
- msg := fmt.Sprintf("unsupported setting GO386=%s", v)
- if v == "387" {
- msg += ". 387 support was dropped in Go 1.16. Consider using gccgo instead."
- }
- log.Fatal(msg)
- }
}
// Note: must agree with runtime.framepointer_enabled.
diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go
index 023429e441..bdbe9df3e7 100644
--- a/src/internal/cfg/cfg.go
+++ b/src/internal/cfg/cfg.go
@@ -32,6 +32,7 @@ const KnownEnv = `
FC
GCCGO
GO111MODULE
+ GO386
GOARCH
GOARM
GOBIN
diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go
index 30f39a8da1..0bdb66a376 100644
--- a/test/codegen/arithmetic.go
+++ b/test/codegen/arithmetic.go
@@ -125,7 +125,7 @@ func Mul_n120(n int) int {
func MulMemSrc(a []uint32, b []float32) {
// 386:`IMULL\s4\([A-Z]+\),\s[A-Z]+`
a[0] *= a[1]
- // 386:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
+ // 386/sse2:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
// amd64:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
b[0] *= b[1]
}
@@ -167,7 +167,7 @@ func MergeMuls5(a, n int) int {
// -------------- //
func DivMemSrc(a []float64) {
- // 386:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
+ // 386/sse2:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
// amd64:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
a[0] /= a[1]
}
@@ -211,7 +211,7 @@ func ConstDivs(n1 uint, n2 int) (uint, int) {
func FloatDivs(a []float32) float32 {
// amd64:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
- // 386:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
+ // 386/sse2:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
return a[1] / a[2]
}
diff --git a/test/codegen/floats.go b/test/codegen/floats.go
index d115800a67..83b4a358a5 100644
--- a/test/codegen/floats.go
+++ b/test/codegen/floats.go
@@ -15,7 +15,7 @@ package codegen
// --------------------- //
func Mul2(f float64) float64 {
- // 386:"ADDSD",-"MULSD"
+ // 386/sse2:"ADDSD",-"MULSD"
// amd64:"ADDSD",-"MULSD"
// arm/7:"ADDD",-"MULD"
// arm64:"FADDD",-"FMULD"
@@ -25,7 +25,7 @@ func Mul2(f float64) float64 {
}
func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
- // 386:"MULSD",-"DIVSD"
+ // 386/sse2:"MULSD",-"DIVSD"
// amd64:"MULSD",-"DIVSD"
// arm/7:"MULD",-"DIVD"
// arm64:"FMULD",-"FDIVD"
@@ -33,7 +33,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
// ppc64le:"FMUL",-"FDIV"
x := f1 / 16.0
- // 386:"MULSD",-"DIVSD"
+ // 386/sse2:"MULSD",-"DIVSD"
// amd64:"MULSD",-"DIVSD"
// arm/7:"MULD",-"DIVD"
// arm64:"FMULD",-"FDIVD"
@@ -41,7 +41,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
// ppc64le:"FMUL",-"FDIVD"
y := f2 / 0.125
- // 386:"ADDSD",-"DIVSD",-"MULSD"
+ // 386/sse2:"ADDSD",-"DIVSD",-"MULSD"
// amd64:"ADDSD",-"DIVSD",-"MULSD"
// arm/7:"ADDD",-"MULD",-"DIVD"
// arm64:"FADDD",-"FMULD",-"FDIVD"
diff --git a/test/codegen/math.go b/test/codegen/math.go
index fe678eea23..ac8071400e 100644
--- a/test/codegen/math.go
+++ b/test/codegen/math.go
@@ -46,7 +46,7 @@ func approx(x float64) {
func sqrt(x float64) float64 {
// amd64:"SQRTSD"
- // 386:"SQRTSD"
+ // 386/sse2:"SQRTSD" 386/softfloat:-"SQRTD"
// arm64:"FSQRTD"
// arm/7:"SQRTD"
// mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD"
diff --git a/test/codegen/memops.go b/test/codegen/memops.go
index 4b003ad861..a234283146 100644
--- a/test/codegen/memops.go
+++ b/test/codegen/memops.go
@@ -175,33 +175,33 @@ func idxInt64(x, y []int64, i int) {
func idxFloat32(x, y []float32, i int) {
var t float32
- // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
- // 386: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ // 386/sse2: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
t = x[i+1]
- // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
- // 386: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
+ // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
+ // 386/sse2: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
y[i+1] = t
- // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
- // 386: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
+ // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
+ // 386/sse2: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
t = x[16*i+1]
- // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
- // 386: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
+ // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
+ // 386/sse2: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
y[16*i+1] = t
}
func idxFloat64(x, y []float64, i int) {
var t float64
- // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
- // 386: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ // 386/sse2: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
t = x[i+1]
- // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
- // 386: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
+ // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
+ // 386/sse2: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
y[i+1] = t
- // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
- // 386: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
+ // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
+ // 386/sse2: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
t = x[16*i+1]
- // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
- // 386: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
+ // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
+ // 386/sse2: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
y[16*i+1] = t
}
diff --git a/test/run.go b/test/run.go
index 77710fd89a..672861c8d7 100644
--- a/test/run.go
+++ b/test/run.go
@@ -1489,7 +1489,7 @@ var (
// value[0] is the variant-changing environment variable, and values[1:]
// are the supported variants.
archVariants = map[string][]string{
- "386": {},
+ "386": {"GO386", "sse2", "softfloat"},
"amd64": {},
"arm": {"GOARM", "5", "6", "7"},
"arm64": {},
@@ -1511,12 +1511,12 @@ type wantedAsmOpcode struct {
found bool // true if the opcode check matched at least one in the output
}
-// A build environment triplet separated by slashes (eg: linux/arm/7).
+// A build environment triplet separated by slashes (eg: linux/386/sse2).
// The third field can be empty if the arch does not support variants (eg: "plan9/amd64/")
type buildEnv string
// Environ returns the environment it represents in cmd.Environ() "key=val" format
-// For instance, "linux/arm/7".Environ() returns {"GOOS=linux", "GOARCH=arm", "GOARM=7"}
+// For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"}
func (b buildEnv) Environ() []string {
fields := strings.Split(string(b), "/")
if len(fields) != 3 {
@@ -1571,11 +1571,11 @@ func (t *test) wantedAsmOpcodes(fn string) asmChecks {
var arch, subarch, os string
switch {
- case archspec[2] != "": // 3 components: "linux/arm/7"
+ case archspec[2] != "": // 3 components: "linux/386/sse2"
os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:]
- case archspec[1] != "": // 2 components: "arm/7"
+ case archspec[1] != "": // 2 components: "386/sse2"
os, arch, subarch = "linux", archspec[0], archspec[1][1:]
- default: // 1 component: "arm"
+ default: // 1 component: "386"
os, arch, subarch = "linux", archspec[0], ""
if arch == "wasm" {
os = "js"
--
cgit v1.2.3-54-g00ecf
From 3923460dda205721d9bee2714a7f0dd403082a90 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Sat, 3 Oct 2020 16:18:43 -0400
Subject: runtime/cgo: only build xx_cgo_panicmem on iOS
On iOS, when running under lldb, we install xx_cgo_panicmem as
EXC_BAD_ACCESS handler so we can get a proper Go panic for
SIGSEGV. Only build it on iOS.
Updates #38485.
Change-Id: I801c477439e05920a4bb8fdf5eae6f4923ab8274
Reviewed-on: https://go-review.googlesource.com/c/go/+/259440
Trust: Cherry Zhang
Reviewed-by: Ian Lance Taylor
---
src/runtime/cgo/gcc_signal2_darwin_arm64.c | 11 --
src/runtime/cgo/gcc_signal2_ios_arm64.c | 11 ++
src/runtime/cgo/gcc_signal_darwin_arm64.c | 213 -----------------------------
src/runtime/cgo/gcc_signal_darwin_lldb.c | 12 --
src/runtime/cgo/gcc_signal_darwin_nolldb.c | 12 ++
src/runtime/cgo/gcc_signal_ios_arm64.c | 213 +++++++++++++++++++++++++++++
src/runtime/cgo/signal_darwin_arm64.go | 10 --
src/runtime/cgo/signal_darwin_arm64.s | 56 --------
src/runtime/cgo/signal_ios_arm64.go | 10 ++
src/runtime/cgo/signal_ios_arm64.s | 56 ++++++++
10 files changed, 302 insertions(+), 302 deletions(-)
delete mode 100644 src/runtime/cgo/gcc_signal2_darwin_arm64.c
create mode 100644 src/runtime/cgo/gcc_signal2_ios_arm64.c
delete mode 100644 src/runtime/cgo/gcc_signal_darwin_arm64.c
delete mode 100644 src/runtime/cgo/gcc_signal_darwin_lldb.c
create mode 100644 src/runtime/cgo/gcc_signal_darwin_nolldb.c
create mode 100644 src/runtime/cgo/gcc_signal_ios_arm64.c
delete mode 100644 src/runtime/cgo/signal_darwin_arm64.go
delete mode 100644 src/runtime/cgo/signal_darwin_arm64.s
create mode 100644 src/runtime/cgo/signal_ios_arm64.go
create mode 100644 src/runtime/cgo/signal_ios_arm64.s
diff --git a/src/runtime/cgo/gcc_signal2_darwin_arm64.c b/src/runtime/cgo/gcc_signal2_darwin_arm64.c
deleted file mode 100644
index 5b8a18ffd6..0000000000
--- a/src/runtime/cgo/gcc_signal2_darwin_arm64.c
+++ /dev/null
@@ -1,11 +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 lldb
-
-// Used by gcc_signal_darwin_arm64.c when doing the test build during cgo.
-// We hope that for real binaries the definition provided by Go will take precedence
-// and the linker will drop this .o file altogether, which is why this definition
-// is all by itself in its own file.
-void __attribute__((weak)) xx_cgo_panicmem(void) {}
diff --git a/src/runtime/cgo/gcc_signal2_ios_arm64.c b/src/runtime/cgo/gcc_signal2_ios_arm64.c
new file mode 100644
index 0000000000..5b8a18ffd6
--- /dev/null
+++ b/src/runtime/cgo/gcc_signal2_ios_arm64.c
@@ -0,0 +1,11 @@
+// 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 lldb
+
+// Used by gcc_signal_darwin_arm64.c when doing the test build during cgo.
+// We hope that for real binaries the definition provided by Go will take precedence
+// and the linker will drop this .o file altogether, which is why this definition
+// is all by itself in its own file.
+void __attribute__((weak)) xx_cgo_panicmem(void) {}
diff --git a/src/runtime/cgo/gcc_signal_darwin_arm64.c b/src/runtime/cgo/gcc_signal_darwin_arm64.c
deleted file mode 100644
index 6519edd4cc..0000000000
--- a/src/runtime/cgo/gcc_signal_darwin_arm64.c
+++ /dev/null
@@ -1,213 +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.
-
-// Emulation of the Unix signal SIGSEGV.
-//
-// On iOS, Go tests and apps under development are run by lldb.
-// The debugger uses a task-level exception handler to intercept signals.
-// Despite having a 'handle' mechanism like gdb, lldb will not allow a
-// SIGSEGV to pass to the running program. For Go, this means we cannot
-// generate a panic, which cannot be recovered, and so tests fail.
-//
-// We work around this by registering a thread-level mach exception handler
-// and intercepting EXC_BAD_ACCESS. The kernel offers thread handlers a
-// chance to resolve exceptions before the task handler, so we can generate
-// the panic and avoid lldb's SIGSEGV handler.
-//
-// The dist tool enables this by build flag when testing.
-
-// +build lldb
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "libcgo.h"
-#include "libcgo_unix.h"
-
-void xx_cgo_panicmem(void);
-uintptr_t x_cgo_panicmem = (uintptr_t)xx_cgo_panicmem;
-
-static pthread_mutex_t mach_exception_handler_port_set_mu;
-static mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL;
-
-kern_return_t
-catch_exception_raise(
- mach_port_t exception_port,
- mach_port_t thread,
- mach_port_t task,
- exception_type_t exception,
- exception_data_t code_vector,
- mach_msg_type_number_t code_count)
-{
- kern_return_t ret;
- arm_unified_thread_state_t thread_state;
- mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT;
-
- // Returning KERN_SUCCESS intercepts the exception.
- //
- // Returning KERN_FAILURE lets the exception fall through to the
- // next handler, which is the standard signal emulation code
- // registered on the task port.
-
- if (exception != EXC_BAD_ACCESS) {
- return KERN_FAILURE;
- }
-
- ret = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, &state_count);
- if (ret) {
- fprintf(stderr, "runtime/cgo: thread_get_state failed: %d\n", ret);
- abort();
- }
-
- // Bounce call to sigpanic through asm that makes it look like
- // we call sigpanic directly from the faulting code.
-#ifdef __arm64__
- thread_state.ts_64.__x[1] = thread_state.ts_64.__lr;
- thread_state.ts_64.__x[2] = thread_state.ts_64.__pc;
- thread_state.ts_64.__pc = x_cgo_panicmem;
-#else
- thread_state.ts_32.__r[1] = thread_state.ts_32.__lr;
- thread_state.ts_32.__r[2] = thread_state.ts_32.__pc;
- thread_state.ts_32.__pc = x_cgo_panicmem;
-#endif
-
- if (0) {
- // Useful debugging logic when panicmem is broken.
- //
- // Sends the first SIGSEGV and lets lldb catch the
- // second one, avoiding a loop that locks up iOS
- // devices requiring a hard reboot.
- fprintf(stderr, "runtime/cgo: caught exc_bad_access\n");
- fprintf(stderr, "__lr = %llx\n", thread_state.ts_64.__lr);
- fprintf(stderr, "__pc = %llx\n", thread_state.ts_64.__pc);
- static int pass1 = 0;
- if (pass1) {
- return KERN_FAILURE;
- }
- pass1 = 1;
- }
-
- ret = thread_set_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, state_count);
- if (ret) {
- fprintf(stderr, "runtime/cgo: thread_set_state failed: %d\n", ret);
- abort();
- }
-
- return KERN_SUCCESS;
-}
-
-void
-darwin_arm_init_thread_exception_port()
-{
- // Called by each new OS thread to bind its EXC_BAD_ACCESS exception
- // to mach_exception_handler_port_set.
- int ret;
- mach_port_t port = MACH_PORT_NULL;
-
- ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
- if (ret) {
- fprintf(stderr, "runtime/cgo: mach_port_allocate failed: %d\n", ret);
- abort();
- }
- ret = mach_port_insert_right(
- mach_task_self(),
- port,
- port,
- MACH_MSG_TYPE_MAKE_SEND);
- if (ret) {
- fprintf(stderr, "runtime/cgo: mach_port_insert_right failed: %d\n", ret);
- abort();
- }
-
- ret = thread_set_exception_ports(
- mach_thread_self(),
- EXC_MASK_BAD_ACCESS,
- port,
- EXCEPTION_DEFAULT,
- THREAD_STATE_NONE);
- if (ret) {
- fprintf(stderr, "runtime/cgo: thread_set_exception_ports failed: %d\n", ret);
- abort();
- }
-
- ret = pthread_mutex_lock(&mach_exception_handler_port_set_mu);
- if (ret) {
- fprintf(stderr, "runtime/cgo: pthread_mutex_lock failed: %d\n", ret);
- abort();
- }
- ret = mach_port_move_member(
- mach_task_self(),
- port,
- mach_exception_handler_port_set);
- if (ret) {
- fprintf(stderr, "runtime/cgo: mach_port_move_member failed: %d\n", ret);
- abort();
- }
- ret = pthread_mutex_unlock(&mach_exception_handler_port_set_mu);
- if (ret) {
- fprintf(stderr, "runtime/cgo: pthread_mutex_unlock failed: %d\n", ret);
- abort();
- }
-}
-
-static void*
-mach_exception_handler(void *port)
-{
- // Calls catch_exception_raise.
- extern boolean_t exc_server();
- mach_msg_server(exc_server, 2048, (mach_port_t)port, 0);
- abort(); // never returns
-}
-
-void
-darwin_arm_init_mach_exception_handler()
-{
- pthread_mutex_init(&mach_exception_handler_port_set_mu, NULL);
-
- // Called once per process to initialize a mach port server, listening
- // for EXC_BAD_ACCESS thread exceptions.
- int ret;
- pthread_t thr = NULL;
- pthread_attr_t attr;
- sigset_t ign, oset;
-
- ret = mach_port_allocate(
- mach_task_self(),
- MACH_PORT_RIGHT_PORT_SET,
- &mach_exception_handler_port_set);
- if (ret) {
- fprintf(stderr, "runtime/cgo: mach_port_allocate failed for port_set: %d\n", ret);
- abort();
- }
-
- // Block all signals to the exception handler thread
- sigfillset(&ign);
- pthread_sigmask(SIG_SETMASK, &ign, &oset);
-
- // Start a thread to handle exceptions.
- uintptr_t port_set = (uintptr_t)mach_exception_handler_port_set;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- ret = _cgo_try_pthread_create(&thr, &attr, mach_exception_handler, (void*)port_set);
-
- pthread_sigmask(SIG_SETMASK, &oset, nil);
-
- if (ret) {
- fprintf(stderr, "runtime/cgo: pthread_create failed: %d\n", ret);
- abort();
- }
- pthread_attr_destroy(&attr);
-}
diff --git a/src/runtime/cgo/gcc_signal_darwin_lldb.c b/src/runtime/cgo/gcc_signal_darwin_lldb.c
deleted file mode 100644
index 0ccdae324e..0000000000
--- a/src/runtime/cgo/gcc_signal_darwin_lldb.c
+++ /dev/null
@@ -1,12 +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 !lldb
-// +build darwin
-// +build arm64
-
-#include
-
-void darwin_arm_init_thread_exception_port() {}
-void darwin_arm_init_mach_exception_handler() {}
diff --git a/src/runtime/cgo/gcc_signal_darwin_nolldb.c b/src/runtime/cgo/gcc_signal_darwin_nolldb.c
new file mode 100644
index 0000000000..26be71bd1d
--- /dev/null
+++ b/src/runtime/cgo/gcc_signal_darwin_nolldb.c
@@ -0,0 +1,12 @@
+// 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 !lldb !ios
+// +build darwin
+// +build arm64
+
+#include
+
+void darwin_arm_init_thread_exception_port() {}
+void darwin_arm_init_mach_exception_handler() {}
diff --git a/src/runtime/cgo/gcc_signal_ios_arm64.c b/src/runtime/cgo/gcc_signal_ios_arm64.c
new file mode 100644
index 0000000000..6519edd4cc
--- /dev/null
+++ b/src/runtime/cgo/gcc_signal_ios_arm64.c
@@ -0,0 +1,213 @@
+// 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.
+
+// Emulation of the Unix signal SIGSEGV.
+//
+// On iOS, Go tests and apps under development are run by lldb.
+// The debugger uses a task-level exception handler to intercept signals.
+// Despite having a 'handle' mechanism like gdb, lldb will not allow a
+// SIGSEGV to pass to the running program. For Go, this means we cannot
+// generate a panic, which cannot be recovered, and so tests fail.
+//
+// We work around this by registering a thread-level mach exception handler
+// and intercepting EXC_BAD_ACCESS. The kernel offers thread handlers a
+// chance to resolve exceptions before the task handler, so we can generate
+// the panic and avoid lldb's SIGSEGV handler.
+//
+// The dist tool enables this by build flag when testing.
+
+// +build lldb
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "libcgo.h"
+#include "libcgo_unix.h"
+
+void xx_cgo_panicmem(void);
+uintptr_t x_cgo_panicmem = (uintptr_t)xx_cgo_panicmem;
+
+static pthread_mutex_t mach_exception_handler_port_set_mu;
+static mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL;
+
+kern_return_t
+catch_exception_raise(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ exception_data_t code_vector,
+ mach_msg_type_number_t code_count)
+{
+ kern_return_t ret;
+ arm_unified_thread_state_t thread_state;
+ mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT;
+
+ // Returning KERN_SUCCESS intercepts the exception.
+ //
+ // Returning KERN_FAILURE lets the exception fall through to the
+ // next handler, which is the standard signal emulation code
+ // registered on the task port.
+
+ if (exception != EXC_BAD_ACCESS) {
+ return KERN_FAILURE;
+ }
+
+ ret = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, &state_count);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: thread_get_state failed: %d\n", ret);
+ abort();
+ }
+
+ // Bounce call to sigpanic through asm that makes it look like
+ // we call sigpanic directly from the faulting code.
+#ifdef __arm64__
+ thread_state.ts_64.__x[1] = thread_state.ts_64.__lr;
+ thread_state.ts_64.__x[2] = thread_state.ts_64.__pc;
+ thread_state.ts_64.__pc = x_cgo_panicmem;
+#else
+ thread_state.ts_32.__r[1] = thread_state.ts_32.__lr;
+ thread_state.ts_32.__r[2] = thread_state.ts_32.__pc;
+ thread_state.ts_32.__pc = x_cgo_panicmem;
+#endif
+
+ if (0) {
+ // Useful debugging logic when panicmem is broken.
+ //
+ // Sends the first SIGSEGV and lets lldb catch the
+ // second one, avoiding a loop that locks up iOS
+ // devices requiring a hard reboot.
+ fprintf(stderr, "runtime/cgo: caught exc_bad_access\n");
+ fprintf(stderr, "__lr = %llx\n", thread_state.ts_64.__lr);
+ fprintf(stderr, "__pc = %llx\n", thread_state.ts_64.__pc);
+ static int pass1 = 0;
+ if (pass1) {
+ return KERN_FAILURE;
+ }
+ pass1 = 1;
+ }
+
+ ret = thread_set_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, state_count);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: thread_set_state failed: %d\n", ret);
+ abort();
+ }
+
+ return KERN_SUCCESS;
+}
+
+void
+darwin_arm_init_thread_exception_port()
+{
+ // Called by each new OS thread to bind its EXC_BAD_ACCESS exception
+ // to mach_exception_handler_port_set.
+ int ret;
+ mach_port_t port = MACH_PORT_NULL;
+
+ ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: mach_port_allocate failed: %d\n", ret);
+ abort();
+ }
+ ret = mach_port_insert_right(
+ mach_task_self(),
+ port,
+ port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: mach_port_insert_right failed: %d\n", ret);
+ abort();
+ }
+
+ ret = thread_set_exception_ports(
+ mach_thread_self(),
+ EXC_MASK_BAD_ACCESS,
+ port,
+ EXCEPTION_DEFAULT,
+ THREAD_STATE_NONE);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: thread_set_exception_ports failed: %d\n", ret);
+ abort();
+ }
+
+ ret = pthread_mutex_lock(&mach_exception_handler_port_set_mu);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: pthread_mutex_lock failed: %d\n", ret);
+ abort();
+ }
+ ret = mach_port_move_member(
+ mach_task_self(),
+ port,
+ mach_exception_handler_port_set);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: mach_port_move_member failed: %d\n", ret);
+ abort();
+ }
+ ret = pthread_mutex_unlock(&mach_exception_handler_port_set_mu);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: pthread_mutex_unlock failed: %d\n", ret);
+ abort();
+ }
+}
+
+static void*
+mach_exception_handler(void *port)
+{
+ // Calls catch_exception_raise.
+ extern boolean_t exc_server();
+ mach_msg_server(exc_server, 2048, (mach_port_t)port, 0);
+ abort(); // never returns
+}
+
+void
+darwin_arm_init_mach_exception_handler()
+{
+ pthread_mutex_init(&mach_exception_handler_port_set_mu, NULL);
+
+ // Called once per process to initialize a mach port server, listening
+ // for EXC_BAD_ACCESS thread exceptions.
+ int ret;
+ pthread_t thr = NULL;
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+
+ ret = mach_port_allocate(
+ mach_task_self(),
+ MACH_PORT_RIGHT_PORT_SET,
+ &mach_exception_handler_port_set);
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: mach_port_allocate failed for port_set: %d\n", ret);
+ abort();
+ }
+
+ // Block all signals to the exception handler thread
+ sigfillset(&ign);
+ pthread_sigmask(SIG_SETMASK, &ign, &oset);
+
+ // Start a thread to handle exceptions.
+ uintptr_t port_set = (uintptr_t)mach_exception_handler_port_set;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ret = _cgo_try_pthread_create(&thr, &attr, mach_exception_handler, (void*)port_set);
+
+ pthread_sigmask(SIG_SETMASK, &oset, nil);
+
+ if (ret) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %d\n", ret);
+ abort();
+ }
+ pthread_attr_destroy(&attr);
+}
diff --git a/src/runtime/cgo/signal_darwin_arm64.go b/src/runtime/cgo/signal_darwin_arm64.go
deleted file mode 100644
index 3425c448c4..0000000000
--- a/src/runtime/cgo/signal_darwin_arm64.go
+++ /dev/null
@@ -1,10 +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.
-
-package cgo
-
-import _ "unsafe"
-
-//go:cgo_export_static xx_cgo_panicmem xx_cgo_panicmem
-func xx_cgo_panicmem()
diff --git a/src/runtime/cgo/signal_darwin_arm64.s b/src/runtime/cgo/signal_darwin_arm64.s
deleted file mode 100644
index 1ae00d13f3..0000000000
--- a/src/runtime/cgo/signal_darwin_arm64.s
+++ /dev/null
@@ -1,56 +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.
-
-#include "textflag.h"
-
-// xx_cgo_panicmem is the entrypoint for SIGSEGV as intercepted via a
-// mach thread port as EXC_BAD_ACCESS. As the segfault may have happened
-// in C code, we first need to load_g then call xx_cgo_panicmem.
-//
-// R1 - LR at moment of fault
-// R2 - PC at moment of fault
-TEXT xx_cgo_panicmem(SB),NOSPLIT|NOFRAME,$0
- // If in external C code, we need to load the g register.
- BL runtime·load_g(SB)
- CMP $0, g
- BNE ongothread
-
- // On a foreign thread.
- // TODO(crawshaw): call badsignal
- MOVD.W $0, -16(RSP)
- MOVW $139, R1
- MOVW R1, 8(RSP)
- B runtime·exit(SB)
-
-ongothread:
- // Trigger a SIGSEGV panic.
- //
- // The goal is to arrange the stack so it looks like the runtime
- // function sigpanic was called from the PC that faulted. It has
- // to be sigpanic, as the stack unwinding code in traceback.go
- // looks explicitly for it.
- //
- // To do this we call into runtime·setsigsegv, which sets the
- // appropriate state inside the g object. We give it the faulting
- // PC on the stack, then put it in the LR before calling sigpanic.
-
- // Build a 32-byte stack frame for us for this call.
- // Saved LR (none available) is at the bottom,
- // then the PC argument for setsigsegv,
- // then a copy of the LR for us to restore.
- MOVD.W $0, -32(RSP)
- MOVD R1, 8(RSP)
- MOVD R2, 16(RSP)
- BL runtime·setsigsegv(SB)
- MOVD 8(RSP), R1
- MOVD 16(RSP), R2
-
- // Build a 16-byte stack frame for the simulated
- // call to sigpanic, by taking 16 bytes away from the
- // 32-byte stack frame above.
- // The saved LR in this frame is the LR at time of fault,
- // and the LR on entry to sigpanic is the PC at time of fault.
- MOVD.W R1, 16(RSP)
- MOVD R2, R30
- B runtime·sigpanic(SB)
diff --git a/src/runtime/cgo/signal_ios_arm64.go b/src/runtime/cgo/signal_ios_arm64.go
new file mode 100644
index 0000000000..3425c448c4
--- /dev/null
+++ b/src/runtime/cgo/signal_ios_arm64.go
@@ -0,0 +1,10 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgo
+
+import _ "unsafe"
+
+//go:cgo_export_static xx_cgo_panicmem xx_cgo_panicmem
+func xx_cgo_panicmem()
diff --git a/src/runtime/cgo/signal_ios_arm64.s b/src/runtime/cgo/signal_ios_arm64.s
new file mode 100644
index 0000000000..1ae00d13f3
--- /dev/null
+++ b/src/runtime/cgo/signal_ios_arm64.s
@@ -0,0 +1,56 @@
+// 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.
+
+#include "textflag.h"
+
+// xx_cgo_panicmem is the entrypoint for SIGSEGV as intercepted via a
+// mach thread port as EXC_BAD_ACCESS. As the segfault may have happened
+// in C code, we first need to load_g then call xx_cgo_panicmem.
+//
+// R1 - LR at moment of fault
+// R2 - PC at moment of fault
+TEXT xx_cgo_panicmem(SB),NOSPLIT|NOFRAME,$0
+ // If in external C code, we need to load the g register.
+ BL runtime·load_g(SB)
+ CMP $0, g
+ BNE ongothread
+
+ // On a foreign thread.
+ // TODO(crawshaw): call badsignal
+ MOVD.W $0, -16(RSP)
+ MOVW $139, R1
+ MOVW R1, 8(RSP)
+ B runtime·exit(SB)
+
+ongothread:
+ // Trigger a SIGSEGV panic.
+ //
+ // The goal is to arrange the stack so it looks like the runtime
+ // function sigpanic was called from the PC that faulted. It has
+ // to be sigpanic, as the stack unwinding code in traceback.go
+ // looks explicitly for it.
+ //
+ // To do this we call into runtime·setsigsegv, which sets the
+ // appropriate state inside the g object. We give it the faulting
+ // PC on the stack, then put it in the LR before calling sigpanic.
+
+ // Build a 32-byte stack frame for us for this call.
+ // Saved LR (none available) is at the bottom,
+ // then the PC argument for setsigsegv,
+ // then a copy of the LR for us to restore.
+ MOVD.W $0, -32(RSP)
+ MOVD R1, 8(RSP)
+ MOVD R2, 16(RSP)
+ BL runtime·setsigsegv(SB)
+ MOVD 8(RSP), R1
+ MOVD 16(RSP), R2
+
+ // Build a 16-byte stack frame for the simulated
+ // call to sigpanic, by taking 16 bytes away from the
+ // 32-byte stack frame above.
+ // The saved LR in this frame is the LR at time of fault,
+ // and the LR on entry to sigpanic is the PC at time of fault.
+ MOVD.W R1, 16(RSP)
+ MOVD R2, R30
+ B runtime·sigpanic(SB)
--
cgit v1.2.3-54-g00ecf
From 7d6b304f123b6d11784b48179059f843493c4790 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Sat, 3 Oct 2020 16:26:37 -0400
Subject: cmd/link: support plugin on macOS/ARM64
Updates #38485.
Change-Id: I8295f7fad55b1f9701162f9d2902b3499137c64d
Reviewed-on: https://go-review.googlesource.com/c/go/+/259441
Trust: Cherry Zhang
Reviewed-by: Than McIntosh
---
src/cmd/dist/test.go | 2 +-
src/cmd/internal/sys/supported.go | 2 +-
src/cmd/link/internal/arm64/asm.go | 20 ++++++++++--
src/cmd/link/internal/ld/config.go | 8 ++++-
src/cmd/link/internal/ld/lib.go | 4 ++-
src/cmd/link/internal/ld/macho.go | 62 ++++++++++++++++++++------------------
6 files changed, 62 insertions(+), 36 deletions(-)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index abe496fdee..4b07501b6d 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -1011,7 +1011,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le":
return true
- case "darwin-amd64":
+ case "darwin-amd64", "darwin-arm64":
return true
case "freebsd-amd64":
return true
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index b2b3b02bf6..94fc92146c 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -104,7 +104,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
switch platform {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
"android/amd64", "android/arm", "android/arm64", "android/386",
- "darwin/amd64",
+ "darwin/amd64", "darwin/arm64",
"freebsd/amd64":
return true
}
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 945b83822c..1d2aa591d7 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -371,7 +371,7 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
rt := r.Type
siz := r.Size
- if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 {
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
if ldr.SymDynid(rs) < 0 {
ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
return false
@@ -415,6 +415,22 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
}
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
+ case objabi.R_ARM64_GOTPCREL:
+ siz = 4
+ // Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21
+ // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff + 4))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ }
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
}
switch siz {
@@ -457,7 +473,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
}
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
- if target.IsDarwin() && rt == objabi.R_ADDRARM64 && xadd != 0 {
+ if target.IsDarwin() && xadd != 0 {
nExtReloc = 4 // need another two relocations for non-zero addend
}
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index 9aa59fa3e3..a3ed5f2307 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -95,7 +95,13 @@ func (mode *BuildMode) Set(s string) error {
default:
return badmode()
}
- case "darwin", "freebsd":
+ case "darwin":
+ switch objabi.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
switch objabi.GOARCH {
case "amd64":
default:
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index cd630e9eae..9fb85becec 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1254,7 +1254,9 @@ func (ctxt *Link) hostlink() {
// -headerpad is incompatible with -fembed-bitcode.
argv = append(argv, "-Wl,-headerpad,1144")
}
- if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) {
+ if ctxt.DynlinkingGo() && objabi.GOOS != "ios" {
+ // -flat_namespace is deprecated on iOS.
+ // It is useful for supporting plugins. We don't support plugins on iOS.
argv = append(argv, "-Wl,-flat_namespace")
}
if !combineDwarf {
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 9765ce18d3..80a753438e 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -76,36 +76,38 @@ const (
)
const (
- MACHO_CPU_AMD64 = 1<<24 | 7
- MACHO_CPU_386 = 7
- MACHO_SUBCPU_X86 = 3
- MACHO_CPU_ARM = 12
- MACHO_SUBCPU_ARM = 0
- MACHO_SUBCPU_ARMV7 = 9
- MACHO_CPU_ARM64 = 1<<24 | 12
- MACHO_SUBCPU_ARM64_ALL = 0
- MACHO32SYMSIZE = 12
- MACHO64SYMSIZE = 16
- MACHO_X86_64_RELOC_UNSIGNED = 0
- MACHO_X86_64_RELOC_SIGNED = 1
- MACHO_X86_64_RELOC_BRANCH = 2
- MACHO_X86_64_RELOC_GOT_LOAD = 3
- MACHO_X86_64_RELOC_GOT = 4
- MACHO_X86_64_RELOC_SUBTRACTOR = 5
- MACHO_X86_64_RELOC_SIGNED_1 = 6
- MACHO_X86_64_RELOC_SIGNED_2 = 7
- MACHO_X86_64_RELOC_SIGNED_4 = 8
- MACHO_ARM_RELOC_VANILLA = 0
- MACHO_ARM_RELOC_PAIR = 1
- MACHO_ARM_RELOC_SECTDIFF = 2
- MACHO_ARM_RELOC_BR24 = 5
- MACHO_ARM64_RELOC_UNSIGNED = 0
- MACHO_ARM64_RELOC_BRANCH26 = 2
- MACHO_ARM64_RELOC_PAGE21 = 3
- MACHO_ARM64_RELOC_PAGEOFF12 = 4
- MACHO_ARM64_RELOC_ADDEND = 10
- MACHO_GENERIC_RELOC_VANILLA = 0
- MACHO_FAKE_GOTPCREL = 100
+ MACHO_CPU_AMD64 = 1<<24 | 7
+ MACHO_CPU_386 = 7
+ MACHO_SUBCPU_X86 = 3
+ MACHO_CPU_ARM = 12
+ MACHO_SUBCPU_ARM = 0
+ MACHO_SUBCPU_ARMV7 = 9
+ MACHO_CPU_ARM64 = 1<<24 | 12
+ MACHO_SUBCPU_ARM64_ALL = 0
+ MACHO32SYMSIZE = 12
+ MACHO64SYMSIZE = 16
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_X86_64_RELOC_BRANCH = 2
+ MACHO_X86_64_RELOC_GOT_LOAD = 3
+ MACHO_X86_64_RELOC_GOT = 4
+ MACHO_X86_64_RELOC_SUBTRACTOR = 5
+ MACHO_X86_64_RELOC_SIGNED_1 = 6
+ MACHO_X86_64_RELOC_SIGNED_2 = 7
+ MACHO_X86_64_RELOC_SIGNED_4 = 8
+ MACHO_ARM_RELOC_VANILLA = 0
+ MACHO_ARM_RELOC_PAIR = 1
+ MACHO_ARM_RELOC_SECTDIFF = 2
+ MACHO_ARM_RELOC_BR24 = 5
+ MACHO_ARM64_RELOC_UNSIGNED = 0
+ MACHO_ARM64_RELOC_BRANCH26 = 2
+ MACHO_ARM64_RELOC_PAGE21 = 3
+ MACHO_ARM64_RELOC_PAGEOFF12 = 4
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
+ MACHO_ARM64_RELOC_ADDEND = 10
+ MACHO_GENERIC_RELOC_VANILLA = 0
+ MACHO_FAKE_GOTPCREL = 100
)
const (
--
cgit v1.2.3-54-g00ecf
From 234de9e1c2afc518e33c0adcf2928a2a9ebf5ce1 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Sat, 3 Oct 2020 23:36:58 -0400
Subject: cmd/link: support PIE on macOS/ARM64
On macOS/ARM64 everything must be PIE, and we already build PIE
in exe buildmode. Support PIE buildmode as well.
Updates #38485.
Change-Id: I10b68c2f6eb77714e31c26116c61a0e28bf9a358
Reviewed-on: https://go-review.googlesource.com/c/go/+/259442
Trust: Cherry Zhang
Reviewed-by: Than McIntosh
---
src/cmd/dist/test.go | 2 +-
src/cmd/internal/sys/supported.go | 2 +-
src/cmd/link/internal/ld/config.go | 8 +++++++-
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 4b07501b6d..d37454c651 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -1023,7 +1023,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
"android-amd64", "android-arm", "android-arm64", "android-386":
return true
- case "darwin-amd64":
+ case "darwin-amd64", "darwin-arm64":
return true
case "windows-amd64", "windows-386", "windows-arm":
return true
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index 94fc92146c..f97f663f2a 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -86,7 +86,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
- "darwin/amd64",
+ "darwin/amd64", "darwin/arm64",
"aix/ppc64",
"windows/386", "windows/amd64", "windows/arm":
return true
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index a3ed5f2307..aaf74b58de 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -39,7 +39,13 @@ func (mode *BuildMode) Set(s string) error {
case "pie":
switch objabi.GOOS {
case "aix", "android", "linux", "windows":
- case "darwin", "freebsd":
+ case "darwin":
+ switch objabi.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
switch objabi.GOARCH {
case "amd64":
default:
--
cgit v1.2.3-54-g00ecf
From f8e554021b7de4bf1150f64d047091b429c92b39 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Sat, 3 Oct 2020 23:58:29 -0400
Subject: cmd/link: support C-shared buildmode on macOS/ARM64
It just works, after the plugin work.
Updates #38485.
Change-Id: I55aa11b380a33a729fccb731b77f48bc7d0dea2e
Reviewed-on: https://go-review.googlesource.com/c/go/+/259443
Trust: Cherry Zhang
Reviewed-by: Than McIntosh
---
src/cmd/dist/test.go | 2 +-
src/cmd/internal/sys/supported.go | 2 +-
src/cmd/link/internal/ld/lib.go | 3 ---
3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index d37454c651..03e6866d62 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -992,7 +992,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
case "c-shared":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
- "darwin-amd64",
+ "darwin-amd64", "darwin-arm64",
"freebsd-amd64",
"android-arm", "android-arm64", "android-386",
"windows-amd64", "windows-386":
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index f97f663f2a..8d87e95655 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -69,7 +69,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
- "darwin/amd64",
+ "darwin/amd64", "darwin/arm64",
"windows/amd64", "windows/386":
return true
}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 9fb85becec..5fe028d321 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1329,9 +1329,6 @@ func (ctxt *Link) hostlink() {
case BuildModeCShared:
if ctxt.HeadType == objabi.Hdarwin {
argv = append(argv, "-dynamiclib")
- if ctxt.Arch.Family != sys.AMD64 {
- argv = append(argv, "-Wl,-read_only_relocs,suppress")
- }
} else {
// ELF.
argv = append(argv, "-Wl,-Bsymbolic")
--
cgit v1.2.3-54-g00ecf
From c19725016df2600a204c9f8447bfcb7dcbdb128a Mon Sep 17 00:00:00 2001
From: Ori Rawlings
Date: Thu, 28 May 2020 22:41:38 -0500
Subject: internal/reflectlite: include Kind in ValueError message
The implementation has been ported from reflect, but to avoid
introducing a dependency on strconv, Kind.String() falls back to
"invalid" if the Kind is unknown rather than "kind" + strconv.Itoa(int(k))
Fixes #39286
Change-Id: I82277242a6c41d0146dabd9d20339fe72d562500
Reviewed-on: https://go-review.googlesource.com/c/go/+/235522
Run-TryBot: Ian Lance Taylor
TryBot-Result: Go Bot
Reviewed-by: Ian Lance Taylor
Reviewed-by: Dmitri Shuralyov
Trust: Dmitri Shuralyov
---
src/internal/reflectlite/type.go | 38 ++++++++++++++++++++++++++++++++++++++
src/internal/reflectlite/value.go | 5 ++++-
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go
index eb7f1a4b78..15ba30da36 100644
--- a/src/internal/reflectlite/type.go
+++ b/src/internal/reflectlite/type.go
@@ -384,6 +384,44 @@ const (
kindMask = (1 << 5) - 1
)
+// String returns the name of k.
+func (k Kind) String() string {
+ if int(k) < len(kindNames) {
+ return kindNames[k]
+ }
+ return kindNames[0]
+}
+
+var kindNames = []string{
+ Invalid: "invalid",
+ Bool: "bool",
+ Int: "int",
+ Int8: "int8",
+ Int16: "int16",
+ Int32: "int32",
+ Int64: "int64",
+ Uint: "uint",
+ Uint8: "uint8",
+ Uint16: "uint16",
+ Uint32: "uint32",
+ Uint64: "uint64",
+ Uintptr: "uintptr",
+ Float32: "float32",
+ Float64: "float64",
+ Complex64: "complex64",
+ Complex128: "complex128",
+ Array: "array",
+ Chan: "chan",
+ Func: "func",
+ Interface: "interface",
+ Map: "map",
+ Ptr: "ptr",
+ Slice: "slice",
+ String: "string",
+ Struct: "struct",
+ UnsafePointer: "unsafe.Pointer",
+}
+
func (t *uncommonType) methods() []method {
if t.mcount == 0 {
return nil
diff --git a/src/internal/reflectlite/value.go b/src/internal/reflectlite/value.go
index 85beea606c..0365eeeabf 100644
--- a/src/internal/reflectlite/value.go
+++ b/src/internal/reflectlite/value.go
@@ -160,7 +160,10 @@ type ValueError struct {
}
func (e *ValueError) Error() string {
- return "reflect: call of " + e.Method + " on zero Value"
+ if e.Kind == 0 {
+ return "reflect: call of " + e.Method + " on zero Value"
+ }
+ return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value"
}
// methodName returns the name of the calling method,
--
cgit v1.2.3-54-g00ecf
From 67edc0ed81947a55adbcd0c9d2317abb93ac9510 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Tue, 6 Oct 2020 22:07:15 -0400
Subject: runtime: restore SSE guard in asyncPreempt on 386
So we don't use SSE instructions under GO386=softfloat.
Change-Id: I8ecc92340ee567f84a22501df2543ec041d25ef2
Reviewed-on: https://go-review.googlesource.com/c/go/+/260137
Trust: Cherry Zhang
Run-TryBot: Cherry Zhang
TryBot-Result: Go Bot
Reviewed-by: Keith Randall
---
src/runtime/mkpreempt.go | 28 ++++++++++++++++++----------
src/runtime/preempt_386.s | 6 ++++++
2 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go
index 40683bb9d9..76237bc31b 100644
--- a/src/runtime/mkpreempt.go
+++ b/src/runtime/mkpreempt.go
@@ -189,26 +189,34 @@ func (l *layout) restore() {
func gen386() {
p("PUSHFL")
-
- // Assign stack offsets.
+ // Save general purpose registers.
var l = layout{sp: "SP"}
for _, reg := range regNames386 {
- if reg == "SP" {
+ if reg == "SP" || strings.HasPrefix(reg, "X") {
continue
}
- if strings.HasPrefix(reg, "X") {
- l.add("MOVUPS", reg, 16)
- } else {
- l.add("MOVL", reg, 4)
- }
+ l.add("MOVL", reg, 4)
}
- p("ADJSP $%d", l.stack)
+ // Save SSE state only if supported.
+ lSSE := layout{stack: l.stack, sp: "SP"}
+ for i := 0; i < 8; i++ {
+ lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
+ }
+
+ p("ADJSP $%d", lSSE.stack)
p("NOP SP")
l.save()
+ p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse")
+ lSSE.save()
+ label("nosse:")
p("CALL ·asyncPreempt2(SB)")
+ p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2")
+ lSSE.restore()
+ label("nosse2:")
l.restore()
- p("ADJSP $%d", -l.stack)
+ p("ADJSP $%d", -lSSE.stack)
+
p("POPFL")
p("RET")
}
diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s
index 5c9b8ea224..c3a5fa1f36 100644
--- a/src/runtime/preempt_386.s
+++ b/src/runtime/preempt_386.s
@@ -14,6 +14,8 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
MOVL BP, 16(SP)
MOVL SI, 20(SP)
MOVL DI, 24(SP)
+ CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1
+ JNE nosse
MOVUPS X0, 28(SP)
MOVUPS X1, 44(SP)
MOVUPS X2, 60(SP)
@@ -22,7 +24,10 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
MOVUPS X5, 108(SP)
MOVUPS X6, 124(SP)
MOVUPS X7, 140(SP)
+nosse:
CALL ·asyncPreempt2(SB)
+ CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1
+ JNE nosse2
MOVUPS 140(SP), X7
MOVUPS 124(SP), X6
MOVUPS 108(SP), X5
@@ -31,6 +36,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
MOVUPS 60(SP), X2
MOVUPS 44(SP), X1
MOVUPS 28(SP), X0
+nosse2:
MOVL 24(SP), DI
MOVL 20(SP), SI
MOVL 16(SP), BP
--
cgit v1.2.3-54-g00ecf
From 0941dc446e6b3028c77158728432086b5c06acf6 Mon Sep 17 00:00:00 2001
From: Eugene Kalinin
Date: Tue, 25 Aug 2020 01:49:39 +0300
Subject: cmd/go: env -w validates GOTMPDIR value
This change makes go env -w check if GOTMPDIR is an absolute path.
If GOTMPDIR is not an absolute and not existing path there will be an
error at every `work.Builder.Init()`. If `go env` has `-u/-w` as
argument `work.Builder.Init()` is not called.
`go env -w GOTMPDIR=` work in the same way as `go env -u GOTMPDIR`.
Fixes #40932
Change-Id: I6b0662302eeace7f20460b6d26c6e59af1111da2
Reviewed-on: https://go-review.googlesource.com/c/go/+/250198
Run-TryBot: Jay Conrod
TryBot-Result: Go Bot
Reviewed-by: Jay Conrod
Trust: Bryan C. Mills
Trust: Jay Conrod
---
src/cmd/go/internal/envcmd/env.go | 24 ++++++++++++++++++++----
src/cmd/go/testdata/script/env_write.txt | 26 ++++++++++++++++++++++++++
2 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index e1f2400f60..59d0ded658 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -203,10 +203,19 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
}
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
- // Only if we're listing all environment variables ("go env")
- // or the variables being requested are in the extra list.
- needCostly := true
- if len(args) > 0 {
+ needCostly := false
+ if *envU || *envW {
+ // We're overwriting or removing default settings,
+ // so it doesn't really matter what the existing settings are.
+ //
+ // Moreover, we haven't validated the new settings yet, so it is
+ // important that we NOT perform any actions based on them,
+ // such as initializing the builder to compute other variables.
+ } else if len(args) == 0 {
+ // We're listing all environment variables ("go env"),
+ // including the expensive ones.
+ needCostly = true
+ } else {
needCostly = false
for _, arg := range args {
switch argKey(arg) {
@@ -269,6 +278,13 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
}
}
+ gotmp, okGOTMP := add["GOTMPDIR"]
+ if okGOTMP {
+ if !filepath.IsAbs(gotmp) && gotmp != "" {
+ base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
+ }
+ }
+
updateEnvFile(add, nil)
return
}
diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt
index 2366c3f580..bdb9bc4077 100644
--- a/src/cmd/go/testdata/script/env_write.txt
+++ b/src/cmd/go/testdata/script/env_write.txt
@@ -24,6 +24,12 @@ stdout GOARCH=
stdout GOOS=
stdout GOROOT=
+# checking errors
+! go env -w
+stderr 'go env -w: no KEY=VALUE arguments given'
+! go env -u
+stderr 'go env -u: no arguments given'
+
# go env -w changes default setting
env root=
[windows] env root=c:
@@ -97,6 +103,26 @@ stderr 'GOPATH entry cannot start with shell metacharacter'
! go env -w GOPATH=./go
stderr 'GOPATH entry is relative; must be absolute path'
+# go env -w rejects invalid GOTMPDIR values
+! go env -w GOTMPDIR=x
+stderr 'go env -w: GOTMPDIR must be an absolute path'
+
+# go env -w should accept absolute GOTMPDIR value
+# and should not create it
+[windows] go env -w GOTMPDIR=$WORK\x\y\z
+[!windows] go env -w GOTMPDIR=$WORK/x/y/z
+! exists $WORK/x/y/z
+# we should be able to clear an env
+go env -u GOTMPDIR
+go env GOTMPDIR
+stdout ^$
+
+[windows] go env -w GOTMPDIR=$WORK\x\y\z
+[!windows] go env -w GOTMPDIR=$WORK/x/y/z
+go env -w GOTMPDIR=
+go env GOTMPDIR
+stdout ^$
+
# go env -w/-u checks validity of GOOS/ARCH combinations
env GOOS=
env GOARCH=
--
cgit v1.2.3-54-g00ecf
From ccf89bef43f3580526019e0804e91352e62047d5 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Wed, 7 Oct 2020 11:32:43 -0400
Subject: cmd/compile: store call args in the call block
We already do this for OpStore, but we didn't do this for OpMove.
Do the same, to ensure that no two memories are live at the same
time.
Fixes #41846.
Change-Id: Iad77ff031b3c4459d1217e0b04aeb0e692eb474d
Reviewed-on: https://go-review.googlesource.com/c/go/+/260237
Trust: Cherry Zhang
Run-TryBot: Cherry Zhang
TryBot-Result: Go Bot
Reviewed-by: David Chase
---
src/cmd/compile/internal/ssa/expand_calls.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 992936b2d3..8c06040542 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -283,7 +283,7 @@ func expandCalls(f *Func) {
// TODO this will be more complicated with registers in the picture.
src := a.Args[0]
dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(auxI), sp)
- if a.Uses == 1 {
+ if a.Uses == 1 && a.Block == v.Block {
a.reset(OpMove)
a.Pos = pos
a.Type = types.TypeMem
@@ -292,7 +292,7 @@ func expandCalls(f *Func) {
a.SetArgs3(dst, src, mem)
mem = a
} else {
- mem = a.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem)
+ mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem)
mem.AuxInt = aux.SizeOfArg(auxI)
}
} else {
--
cgit v1.2.3-54-g00ecf
From 492258549717d4e73a22170c507fb26a731c4aba Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor
Date: Tue, 6 Oct 2020 16:31:00 -0700
Subject: syscall: rewrite Windows makeCmdLine to use []byte
It's faster to append to a []byte and only convert to string at the
end then it is to build up a string by concatenating characters.
Fixes #41825
Change-Id: I45ddf77dcc62726c919f0533c95d483cee8ba366
Reviewed-on: https://go-review.googlesource.com/c/go/+/259978
Trust: Ian Lance Taylor
Trust: Alex Brainman
Run-TryBot: Ian Lance Taylor
TryBot-Result: Go Bot
Reviewed-by: Alex Brainman
---
src/syscall/exec_windows.go | 73 ++++++++++++++++++++++++++-------------------
1 file changed, 43 insertions(+), 30 deletions(-)
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 8d6141c0ca..500321ef0d 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -24,74 +24,87 @@ var ForkLock sync.RWMutex
// - finally, s is wrapped with double quotes (arg -> "arg"),
// but only if there is space or tab inside s.
func EscapeArg(s string) string {
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '"', '\\', ' ', '\t':
+ // Some escaping required.
+ b := make([]byte, 0, len(s)+2)
+ b = appendEscapeArg(b, s)
+ return string(b)
+ }
+ }
+ return s
+}
+
+// appendEscapeArg escapes the string s, as per escapeArg,
+// appends the result to b, and returns the updated slice.
+func appendEscapeArg(b []byte, s string) []byte {
if len(s) == 0 {
- return "\"\""
+ return append(b, `""`...)
}
- n := len(s)
+
+ needsBackslash := false
hasSpace := false
for i := 0; i < len(s); i++ {
switch s[i] {
case '"', '\\':
- n++
+ needsBackslash = true
case ' ', '\t':
hasSpace = true
}
}
- if hasSpace {
- n += 2
+
+ if !needsBackslash && !hasSpace {
+ // No special handling required; normal case.
+ return append(b, s...)
}
- if n == len(s) {
- return s
+ if !needsBackslash {
+ // hasSpace is true, so we need to quote the string.
+ b = append(b, '"')
+ b = append(b, s...)
+ return append(b, '"')
}
- qs := make([]byte, n)
- j := 0
if hasSpace {
- qs[j] = '"'
- j++
+ b = append(b, '"')
}
slashes := 0
for i := 0; i < len(s); i++ {
- switch s[i] {
+ c := s[i]
+ switch c {
default:
slashes = 0
- qs[j] = s[i]
case '\\':
slashes++
- qs[j] = s[i]
case '"':
for ; slashes > 0; slashes-- {
- qs[j] = '\\'
- j++
+ b = append(b, '\\')
}
- qs[j] = '\\'
- j++
- qs[j] = s[i]
+ b = append(b, '\\')
}
- j++
+ b = append(b, c)
}
if hasSpace {
for ; slashes > 0; slashes-- {
- qs[j] = '\\'
- j++
+ b = append(b, '\\')
}
- qs[j] = '"'
- j++
+ b = append(b, '"')
}
- return string(qs[:j])
+
+ return b
}
// makeCmdLine builds a command line out of args by escaping "special"
// characters and joining the arguments with spaces.
func makeCmdLine(args []string) string {
- var s string
+ var b []byte
for _, v := range args {
- if s != "" {
- s += " "
+ if len(b) > 0 {
+ b = append(b, ' ')
}
- s += EscapeArg(v)
+ b = appendEscapeArg(b, v)
}
- return s
+ return string(b)
}
// createEnvBlock converts an array of environment strings into
--
cgit v1.2.3-54-g00ecf
From 5c1567cdc064b68210aeeddc6bf76bf0a146a626 Mon Sep 17 00:00:00 2001
From: Ayan George
Date: Tue, 6 Oct 2020 18:40:40 +0000
Subject: net/http/pprof: use Request.Context, not the deprecated CloseNotifier
Prior to this commit, the profiling code had a sleep() function that
waits and unblocks on either time.After() or a channel provided by an
http.CloseNotifier derived from a supplied http.ResponseWriter.
According to the documentation, http.CloseNotifier is deprecated:
Deprecated: the CloseNotifier interface predates Go's context package.
New code should use Request.Context instead.
This patch does just that -- sleep() now takes an *http.Request and uses
http.Request.Context() to signal when a request has been cancelled.
Change-Id: I98702314addf494f5743a4f99172dc607389dbb8
GitHub-Last-Rev: c1e37a03ca28417ed5833618d3eeddb2eecccd09
GitHub-Pull-Request: golang/go#41756
Reviewed-on: https://go-review.googlesource.com/c/go/+/259157
Reviewed-by: Bryan C. Mills
Reviewed-by: Hyang-Ah Hana Kim
Reviewed-by: Emmanuel Odeke