From a413908dd064de6e3ea5b8d95d707a532bd3f4c8 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Wed, 16 Sep 2020 16:59:58 -0400 Subject: all: add GOOS=ios Introduce GOOS=ios for iOS systems. GOOS=ios matches "darwin" build tag, like GOOS=android matches "linux" and GOOS=illumos matches "solaris". Only ios/arm64 is supported (ios/amd64 is not). GOOS=ios and GOOS=darwin remain essentially the same at this point. They will diverge at later time, to differentiate macOS and iOS. Uses of GOOS=="darwin" are changed to (GOOS=="darwin" || GOOS=="ios"), except if it clearly means macOS (e.g. GOOS=="darwin" && GOARCH=="amd64"), it remains GOOS=="darwin". Updates #38485. Change-Id: I4faacdc1008f42434599efb3c3ad90763a83b67c Reviewed-on: https://go-review.googlesource.com/c/go/+/254740 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Austin Clements --- src/cmd/dist/build.go | 11 ++++++++--- src/cmd/dist/test.go | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 397b3bb88f..3ac742fa55 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -80,6 +80,7 @@ var okgoos = []string{ "darwin", "dragonfly", "illumos", + "ios", "js", "linux", "android", @@ -970,7 +971,10 @@ func matchtag(tag string) bool { } return !matchtag(tag[1:]) } - return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") || (goos == "illumos" && tag == "solaris") + return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || + (goos == "android" && tag == "linux") || + (goos == "illumos" && tag == "solaris") || + (goos == "ios" && tag == "darwin") } // shouldbuild reports whether we should build this file. @@ -984,7 +988,7 @@ func shouldbuild(file, pkg string) bool { name := filepath.Base(file) excluded := func(list []string, ok string) bool { for _, x := range list { - if x == ok || (ok == "android" && x == "linux") || (ok == "illumos" && x == "solaris") { + if x == ok || (ok == "android" && x == "linux") || (ok == "illumos" && x == "solaris") || (ok == "ios" && x == "darwin") { continue } i := strings.Index(name, x) @@ -1462,7 +1466,7 @@ func wrapperPathFor(goos, goarch string) string { if gohostos != "android" { return pathf("%s/misc/android/go_android_exec.go", goroot) } - case goos == "darwin" && goarch == "arm64": + case (goos == "darwin" || goos == "ios") && goarch == "arm64": if gohostos != "darwin" || gohostarch != "arm64" { return pathf("%s/misc/ios/go_darwin_arm_exec.go", goroot) } @@ -1541,6 +1545,7 @@ var cgoEnabled = map[string]bool{ "android/amd64": true, "android/arm": true, "android/arm64": true, + "ios/arm64": true, "js/wasm": false, "netbsd/386": true, "netbsd/amd64": true, diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 5ea5c81656..f953a76963 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -464,7 +464,7 @@ func (t *tester) registerTests() { } // Test the ios build tag on darwin/amd64 for the iOS simulator. - if goos == "darwin" && !t.iOS() { + if goos == "darwin" && goarch == "amd64" { t.tests = append(t.tests, distTest{ name: "amd64ios", heading: "ios tag on darwin/amd64", @@ -903,7 +903,7 @@ func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec. } func (t *tester) iOS() bool { - return goos == "darwin" && goarch == "arm64" + return (goos == "darwin" || goos == "ios") && goarch == "arm64" } func (t *tester) out(v string) { @@ -943,7 +943,10 @@ func (t *tester) internalLink() bool { if goos == "android" { return false } - if t.iOS() { + if goos == "ios" { + return false + } + if goos == "darwin" && goarch == "arm64" { return false } // Internally linking cgo is incomplete on some architectures. -- cgit v1.3 From ad618689ef06e9dca86c0e2b9b38a2c1b9266f4a Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Sat, 26 Sep 2020 16:30:02 +0200 Subject: cmd/dist: detect gohostarch on netbsd/arm64 hosts On netbsd/arm64 `uname -m` reports `evbarm` which is mapped to gohostarch=arm. Fix this by checking for "aarch64" in `uname -p` output to fix self-hosted build on netbsd/arm64. Updates #30824 Change-Id: I0f0450ff35af0bec51aeec3b210143ba892216c6 Reviewed-on: https://go-review.googlesource.com/c/go/+/257643 Trust: Tobias Klauser Trust: Benny Siegert Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Benny Siegert --- src/cmd/dist/main.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go index b8a8c5f2e6..224b6c0c3e 100644 --- a/src/cmd/dist/main.go +++ b/src/cmd/dist/main.go @@ -108,6 +108,9 @@ func main() { gohostarch = "arm64" case strings.Contains(out, "arm"): gohostarch = "arm" + if gohostos == "netbsd" && strings.Contains(run("", CheckExit, "uname", "-p"), "aarch64") { + gohostarch = "arm64" + } case strings.Contains(out, "ppc64le"): gohostarch = "ppc64le" case strings.Contains(out, "ppc64"): -- cgit v1.3 From fe2cfb74ba6352990f5b41260b99e80f78e4a90a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 1 Oct 2020 14:49:33 -0700 Subject: all: drop 387 support My last 387 CL. So sad ... ... ... ... not! Fixes #40255 Change-Id: I8d4ddb744b234b8adc735db2f7c3c7b6d8bbdfa4 Reviewed-on: https://go-review.googlesource.com/c/go/+/258957 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/asm/internal/asm/endtoend_test.go | 7 +- src/cmd/compile/internal/gc/float_test.go | 19 -- src/cmd/compile/internal/gc/go.go | 5 - src/cmd/compile/internal/gc/ssa.go | 15 +- src/cmd/compile/internal/ssa/config.go | 6 - src/cmd/compile/internal/ssa/gen/386.rules | 10 +- src/cmd/compile/internal/ssa/gen/386Ops.go | 14 - src/cmd/compile/internal/ssa/opGen.go | 13 - src/cmd/compile/internal/ssa/regalloc.go | 14 - src/cmd/compile/internal/ssa/rewrite386.go | 84 ++---- src/cmd/compile/internal/x86/387.go | 403 ----------------------------- src/cmd/compile/internal/x86/galign.go | 17 +- src/cmd/compile/internal/x86/ssa.go | 2 - src/cmd/dist/build.go | 15 -- src/cmd/dist/buildruntime.go | 2 - src/cmd/dist/cpuid_386.s | 16 -- src/cmd/dist/cpuid_amd64.s | 16 -- src/cmd/dist/cpuid_default.s | 10 - src/cmd/dist/util_gc.go | 12 - src/cmd/dist/util_gccgo.go | 13 - src/cmd/go/alldocs.go | 3 - src/cmd/go/internal/cfg/cfg.go | 3 - src/cmd/go/internal/envcmd/env.go | 5 +- src/cmd/go/internal/help/helpdoc.go | 3 - src/cmd/go/internal/work/exec.go | 4 +- src/cmd/internal/objabi/util.go | 9 +- src/internal/cfg/cfg.go | 1 - src/reflect/all_test.go | 18 -- src/runtime/mkpreempt.go | 33 +-- src/runtime/preempt_386.s | 45 ++-- src/runtime/vlrt.go | 5 +- test/codegen/arithmetic.go | 6 +- test/codegen/floats.go | 19 +- test/codegen/math.go | 2 +- test/codegen/memops.go | 32 +-- test/run.go | 12 +- 36 files changed, 97 insertions(+), 796 deletions(-) delete mode 100644 src/cmd/compile/internal/x86/387.go delete mode 100644 src/cmd/dist/cpuid_386.s delete mode 100644 src/cmd/dist/cpuid_amd64.s delete mode 100644 src/cmd/dist/cpuid_default.s (limited to 'src/cmd/dist') diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index 0759b7d10f..15202dc5dc 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -353,12 +353,7 @@ func testErrors(t *testing.T, goarch, file string) { } func Test386EndToEnd(t *testing.T) { - defer func(old string) { objabi.GO386 = old }(objabi.GO386) - for _, go386 := range []string{"387", "sse2"} { - t.Logf("GO386=%v", go386) - objabi.GO386 = go386 - testEndToEnd(t, "386", "386") - } + testEndToEnd(t, "386", "386") } func TestARMEndToEnd(t *testing.T) { diff --git a/src/cmd/compile/internal/gc/float_test.go b/src/cmd/compile/internal/gc/float_test.go index 6ae363be22..c619d25705 100644 --- a/src/cmd/compile/internal/gc/float_test.go +++ b/src/cmd/compile/internal/gc/float_test.go @@ -6,17 +6,9 @@ package gc import ( "math" - "os" - "runtime" "testing" ) -// For GO386=387, make sure fucomi* opcodes are not used -// for comparison operations. -// Note that this test will fail only on a Pentium MMX -// processor (with GOARCH=386 GO386=387), as it just runs -// some code and looks for an unimplemented instruction fault. - //go:noinline func compare1(a, b float64) bool { return a < b @@ -137,9 +129,6 @@ func TestFloatCompareFolded(t *testing.T) { } } -// For GO386=387, make sure fucomi* opcodes are not used -// for float->int conversions. - //go:noinline func cvt1(a float64) uint64 { return uint64(a) @@ -370,14 +359,6 @@ func TestFloat32StoreToLoadConstantFold(t *testing.T) { // are not converted to quiet NaN (qNaN) values during compilation. // See issue #27193 for more information. - // TODO: this method for detecting 387 won't work if the compiler has been - // built using GOARCH=386 GO386=387 and either the target is a different - // architecture or the GO386=387 environment variable is not set when the - // test is run. - if runtime.GOARCH == "386" && os.Getenv("GO386") == "387" { - t.Skip("signaling NaNs are not propagated on 387 (issue #27516)") - } - // signaling NaNs { const nan = uint32(0x7f800001) // sNaN diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 9079ce2afc..2fbdf71055 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -259,7 +259,6 @@ type Arch struct { REGSP int MAXWIDTH int64 - Use387 bool // should 386 backend use 387 FP instructions instead of sse2. SoftFloat bool PadFrame func(int64) int64 @@ -328,10 +327,6 @@ var ( BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym - // GO386=387 - ControlWord64trunc, - ControlWord32 *obj.LSym - // Wasm WasmMove, WasmZero, diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 1d50cefe54..32394c4b1a 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -62,9 +62,6 @@ func initssaconfig() { _ = types.NewPtr(types.Errortype) // *error types.NewPtrCacheEnabled = false ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug['N'] == 0) - if thearch.LinkArch.Name == "386" { - ssaConfig.Set387(thearch.Use387) - } ssaConfig.SoftFloat = thearch.SoftFloat ssaConfig.Race = flag_race ssaCaches = make([]ssa.Cache, nBackendWorkers) @@ -175,10 +172,6 @@ func initssaconfig() { ExtendCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicExtendSlice3CU") } - // GO386=387 runtime definitions - ControlWord64trunc = sysvar("controlWord64trunc") // uint16 - ControlWord32 = sysvar("controlWord32") // uint16 - // Wasm (all asm funcs with special ABIs) WasmMove = sysvar("wasmMove") WasmZero = sysvar("wasmZero") @@ -5946,9 +5939,7 @@ type SSAGenState struct { // bstart remembers where each block starts (indexed by block ID) bstart []*obj.Prog - // 387 port: maps from SSE registers (REG_X?) to 387 registers (REG_F?) - SSEto387 map[int16]int16 - // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include x86-387, PPC, and Sparc V8. + // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8. ScratchFpMem *Node maxarg int64 // largest frame size for arguments to calls made by the function @@ -6115,10 +6106,6 @@ func genssa(f *ssa.Func, pp *Progs) { progToBlock[s.pp.next] = f.Blocks[0] } - if thearch.Use387 { - s.SSEto387 = map[int16]int16{} - } - s.ScratchFpMem = e.scratchFpMem if Ctxt.Flag_locationlists { diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 88a406deb9..649b5ba820 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -38,7 +38,6 @@ type Config struct { useSSE bool // Use SSE for non-float operations useAvg bool // Use optimizations that need Avg* operations useHmul bool // Use optimizations that need Hmul* operations - use387 bool // GO386=387 SoftFloat bool // Race bool // race detector enabled NeedsFpScratch bool // No direct move between GP and FP register sets @@ -387,9 +386,4 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config return c } -func (c *Config) Set387(b bool) { - c.NeedsFpScratch = b - c.use387 = b -} - func (c *Config) Ctxt() *obj.Link { return c.ctxt } diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules index 4a8244eb27..6a0b87cab4 100644 --- a/src/cmd/compile/internal/ssa/gen/386.rules +++ b/src/cmd/compile/internal/ssa/gen/386.rules @@ -38,10 +38,8 @@ (Xor(32|16|8) ...) => (XORL ...) (Neg(32|16|8) ...) => (NEGL ...) -(Neg32F x) && !config.use387 => (PXOR x (MOVSSconst [float32(math.Copysign(0, -1))])) -(Neg64F x) && !config.use387 => (PXOR x (MOVSDconst [math.Copysign(0, -1)])) -(Neg32F x) && config.use387 => (FCHS x) -(Neg64F x) && config.use387 => (FCHS x) +(Neg32F x) => (PXOR x (MOVSSconst [float32(math.Copysign(0, -1))])) +(Neg64F x) => (PXOR x (MOVSDconst [math.Copysign(0, -1)])) (Com(32|16|8) ...) => (NOTL ...) @@ -670,8 +668,8 @@ // Merge load/store to op ((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|AND|OR|XOR|SUB|MUL)Lload x [off] {sym} ptr mem) -((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) => ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem) -((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) => ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem) +((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem) +((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem) (MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lload x [off] {sym} ptr mem) mem) && y.Uses==1 && clobber(y) => ((ADD|AND|OR|XOR)Lmodify [off] {sym} ptr x mem) (MOVLstore {sym} [off] ptr y:((ADD|SUB|AND|OR|XOR)L l:(MOVLload [off] {sym} ptr mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y, l) => ((ADD|SUB|AND|OR|XOR)Lmodify [off] {sym} ptr x mem) diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index ddabde7d3d..737b99c371 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -51,17 +51,6 @@ var regNames386 = []string{ "SB", } -// Notes on 387 support. -// - The 387 has a weird stack-register setup for floating-point registers. -// We use these registers when SSE registers are not available (when GO386=387). -// - We use the same register names (X0-X7) but they refer to the 387 -// floating-point registers. That way, most of the SSA backend is unchanged. -// - The instruction generation pass maintains an SSE->387 register mapping. -// This mapping is updated whenever the FP stack is pushed or popped so that -// we can always find a given SSE register even when the TOS pointer has changed. -// - To facilitate the mapping from SSE to 387, we enforce that -// every basic block starts and ends with an empty floating-point stack. - func init() { // Make map from reg names to reg integers. if len(regNames386) > 64 { @@ -552,9 +541,6 @@ func init() { {name: "FlagGT_UGT"}, // signed > and unsigned < {name: "FlagGT_ULT"}, // signed > and unsigned > - // Special op for -x on 387 - {name: "FCHS", argLength: 1, reg: fp11}, - // Special ops for PIC floating-point constants. // MOVSXconst1 loads the address of the constant-pool entry into a register. // MOVSXconst2 loads the constant from that address. diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 9fe943c2e0..d7d2b24a48 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -536,7 +536,6 @@ const ( Op386FlagLT_UGT Op386FlagGT_UGT Op386FlagGT_ULT - Op386FCHS Op386MOVSSconst1 Op386MOVSDconst1 Op386MOVSSconst2 @@ -6060,18 +6059,6 @@ var opcodeTable = [...]opInfo{ argLen: 0, reg: regInfo{}, }, - { - name: "FCHS", - argLen: 1, - reg: regInfo{ - inputs: []inputInfo{ - {0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7 - }, - outputs: []outputInfo{ - {0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7 - }, - }, - }, { name: "MOVSSconst1", auxType: auxFloat32, diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 64c6aed3e7..691530ec0b 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -625,9 +625,6 @@ func (s *regAllocState) init(f *Func) { s.f.fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.arch) } } - if s.f.Config.use387 { - s.allocatable &^= 1 << 15 // X7 disallowed (one 387 register is used as scratch space during SSE->387 generation in ../x86/387.go) - } // Linear scan register allocation can be influenced by the order in which blocks appear. // Decouple the register allocation order from the generated block order. @@ -1024,9 +1021,6 @@ func (s *regAllocState) regalloc(f *Func) { if phiRegs[i] != noRegister { continue } - if s.f.Config.use387 && v.Type.IsFloat() { - continue // 387 can't handle floats in registers between blocks - } m := s.compatRegs(v.Type) &^ phiUsed &^ s.used if m != 0 { r := pickReg(m) @@ -1528,11 +1522,6 @@ func (s *regAllocState) regalloc(f *Func) { s.freeUseRecords = u } - // Spill any values that can't live across basic block boundaries. - if s.f.Config.use387 { - s.freeRegs(s.f.Config.fpRegMask) - } - // If we are approaching a merge point and we are the primary // predecessor of it, find live values that we use soon after // the merge point and promote them to registers now. @@ -1562,9 +1551,6 @@ func (s *regAllocState) regalloc(f *Func) { continue } v := s.orig[vid] - if s.f.Config.use387 && v.Type.IsFloat() { - continue // 387 can't handle floats in registers between blocks - } m := s.compatRegs(v.Type) &^ s.used if m&^desired.avoid != 0 { m &^= desired.avoid diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go index fc1e0541b2..0f08160f44 100644 --- a/src/cmd/compile/internal/ssa/rewrite386.go +++ b/src/cmd/compile/internal/ssa/rewrite386.go @@ -1310,10 +1310,8 @@ func rewriteValue386_Op386ADDLmodify(v *Value) bool { func rewriteValue386_Op386ADDSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (ADDSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (ADDSDload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1326,7 +1324,7 @@ func rewriteValue386_Op386ADDSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386ADDSDload) @@ -1395,10 +1393,8 @@ func rewriteValue386_Op386ADDSDload(v *Value) bool { func rewriteValue386_Op386ADDSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (ADDSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (ADDSSload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1411,7 +1407,7 @@ func rewriteValue386_Op386ADDSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386ADDSSload) @@ -2640,10 +2636,8 @@ func rewriteValue386_Op386CMPWload(v *Value) bool { func rewriteValue386_Op386DIVSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (DIVSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (DIVSDload x [off] {sym} ptr mem) for { x := v_0 @@ -2655,7 +2649,7 @@ func rewriteValue386_Op386DIVSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386DIVSDload) @@ -2722,10 +2716,8 @@ func rewriteValue386_Op386DIVSDload(v *Value) bool { func rewriteValue386_Op386DIVSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (DIVSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (DIVSSload x [off] {sym} ptr mem) for { x := v_0 @@ -2737,7 +2729,7 @@ func rewriteValue386_Op386DIVSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386DIVSSload) @@ -6104,10 +6096,8 @@ func rewriteValue386_Op386MULLload(v *Value) bool { func rewriteValue386_Op386MULSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (MULSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (MULSDload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -6120,7 +6110,7 @@ func rewriteValue386_Op386MULSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386MULSDload) @@ -6189,10 +6179,8 @@ func rewriteValue386_Op386MULSDload(v *Value) bool { func rewriteValue386_Op386MULSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (MULSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (MULSSload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -6205,7 +6193,7 @@ func rewriteValue386_Op386MULSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386MULSSload) @@ -8187,10 +8175,8 @@ func rewriteValue386_Op386SUBLmodify(v *Value) bool { func rewriteValue386_Op386SUBSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (SUBSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (SUBSDload x [off] {sym} ptr mem) for { x := v_0 @@ -8202,7 +8188,7 @@ func rewriteValue386_Op386SUBSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386SUBSDload) @@ -8269,10 +8255,8 @@ func rewriteValue386_Op386SUBSDload(v *Value) bool { func rewriteValue386_Op386SUBSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (SUBSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (SUBSSload x [off] {sym} ptr mem) for { x := v_0 @@ -8284,7 +8268,7 @@ func rewriteValue386_Op386SUBSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386SUBSSload) @@ -10043,68 +10027,32 @@ func rewriteValue386_OpMove(v *Value) bool { func rewriteValue386_OpNeg32F(v *Value) bool { v_0 := v.Args[0] b := v.Block - config := b.Func.Config typ := &b.Func.Config.Types // match: (Neg32F x) - // cond: !config.use387 // result: (PXOR x (MOVSSconst [float32(math.Copysign(0, -1))])) for { x := v_0 - if !(!config.use387) { - break - } v.reset(Op386PXOR) v0 := b.NewValue0(v.Pos, Op386MOVSSconst, typ.Float32) v0.AuxInt = float32ToAuxInt(float32(math.Copysign(0, -1))) v.AddArg2(x, v0) return true } - // match: (Neg32F x) - // cond: config.use387 - // result: (FCHS x) - for { - x := v_0 - if !(config.use387) { - break - } - v.reset(Op386FCHS) - v.AddArg(x) - return true - } - return false } func rewriteValue386_OpNeg64F(v *Value) bool { v_0 := v.Args[0] b := v.Block - config := b.Func.Config typ := &b.Func.Config.Types // match: (Neg64F x) - // cond: !config.use387 // result: (PXOR x (MOVSDconst [math.Copysign(0, -1)])) for { x := v_0 - if !(!config.use387) { - break - } v.reset(Op386PXOR) v0 := b.NewValue0(v.Pos, Op386MOVSDconst, typ.Float64) v0.AuxInt = float64ToAuxInt(math.Copysign(0, -1)) v.AddArg2(x, v0) return true } - // match: (Neg64F x) - // cond: config.use387 - // result: (FCHS x) - for { - x := v_0 - if !(config.use387) { - break - } - v.reset(Op386FCHS) - v.AddArg(x) - return true - } - return false } func rewriteValue386_OpNeq16(v *Value) bool { v_1 := v.Args[1] diff --git a/src/cmd/compile/internal/x86/387.go b/src/cmd/compile/internal/x86/387.go deleted file mode 100644 index 594adb2cd5..0000000000 --- a/src/cmd/compile/internal/x86/387.go +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package x86 - -import ( - "cmd/compile/internal/gc" - "cmd/compile/internal/ssa" - "cmd/compile/internal/types" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "math" -) - -// Generates code for v using 387 instructions. -func ssaGenValue387(s *gc.SSAGenState, v *ssa.Value) { - // The SSA compiler pretends that it has an SSE backend. - // If we don't have one of those, we need to translate - // all the SSE ops to equivalent 387 ops. That's what this - // function does. - - switch v.Op { - case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst: - iv := uint64(v.AuxInt) - if iv == 0x0000000000000000 { // +0.0 - s.Prog(x86.AFLDZ) - } else if iv == 0x3ff0000000000000 { // +1.0 - s.Prog(x86.AFLD1) - } else if iv == 0x8000000000000000 { // -0.0 - s.Prog(x86.AFLDZ) - s.Prog(x86.AFCHS) - } else if iv == 0xbff0000000000000 { // -1.0 - s.Prog(x86.AFLD1) - s.Prog(x86.AFCHS) - } else if iv == 0x400921fb54442d18 { // +pi - s.Prog(x86.AFLDPI) - } else if iv == 0xc00921fb54442d18 { // -pi - s.Prog(x86.AFLDPI) - s.Prog(x86.AFCHS) - } else { // others - p := s.Prog(loadPush(v.Type)) - p.From.Type = obj.TYPE_FCONST - p.From.Val = math.Float64frombits(iv) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - } - popAndSave(s, v) - - case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2: - p := s.Prog(loadPush(v.Type)) - p.From.Type = obj.TYPE_MEM - p.From.Reg = v.Args[0].Reg() - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1, ssa.Op386MOVSSloadidx4, ssa.Op386MOVSDloadidx8: - p := s.Prog(loadPush(v.Type)) - p.From.Type = obj.TYPE_MEM - p.From.Reg = v.Args[0].Reg() - gc.AddAux(&p.From, v) - switch v.Op { - case ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1: - p.From.Scale = 1 - p.From.Index = v.Args[1].Reg() - if p.From.Index == x86.REG_SP { - p.From.Reg, p.From.Index = p.From.Index, p.From.Reg - } - case ssa.Op386MOVSSloadidx4: - p.From.Scale = 4 - p.From.Index = v.Args[1].Reg() - case ssa.Op386MOVSDloadidx8: - p.From.Scale = 8 - p.From.Index = v.Args[1].Reg() - } - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore: - // Push to-be-stored value on top of stack. - push(s, v.Args[1]) - - // Pop and store value. - var op obj.As - switch v.Op { - case ssa.Op386MOVSSstore: - op = x86.AFMOVFP - case ssa.Op386MOVSDstore: - op = x86.AFMOVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_MEM - p.To.Reg = v.Args[0].Reg() - gc.AddAux(&p.To, v) - - case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVSDstoreidx8: - push(s, v.Args[2]) - var op obj.As - switch v.Op { - case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSSstoreidx4: - op = x86.AFMOVFP - case ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSDstoreidx8: - op = x86.AFMOVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_MEM - p.To.Reg = v.Args[0].Reg() - gc.AddAux(&p.To, v) - switch v.Op { - case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1: - p.To.Scale = 1 - p.To.Index = v.Args[1].Reg() - if p.To.Index == x86.REG_SP { - p.To.Reg, p.To.Index = p.To.Index, p.To.Reg - } - case ssa.Op386MOVSSstoreidx4: - p.To.Scale = 4 - p.To.Index = v.Args[1].Reg() - case ssa.Op386MOVSDstoreidx8: - p.To.Scale = 8 - p.To.Index = v.Args[1].Reg() - } - - case ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD, - ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD: - if v.Reg() != v.Args[0].Reg() { - v.Fatalf("input[0] and output not in same register %s", v.LongString()) - } - - // Push arg1 on top of stack - push(s, v.Args[1]) - - // Set precision if needed. 64 bits is the default. - switch v.Op { - case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS: - // Save AX so we can use it as scratch space. - p := s.Prog(x86.AMOVL) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_AX - s.AddrScratch(&p.To) - // Install a 32-bit version of the control word. - installControlWord(s, gc.ControlWord32, x86.REG_AX) - // Restore AX. - p = s.Prog(x86.AMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_AX - } - - var op obj.As - switch v.Op { - case ssa.Op386ADDSS, ssa.Op386ADDSD: - op = x86.AFADDDP - case ssa.Op386SUBSS, ssa.Op386SUBSD: - op = x86.AFSUBDP - case ssa.Op386MULSS, ssa.Op386MULSD: - op = x86.AFMULDP - case ssa.Op386DIVSS, ssa.Op386DIVSD: - op = x86.AFDIVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = s.SSEto387[v.Reg()] + 1 - - // Restore precision if needed. - switch v.Op { - case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS: - restoreControlWord(s) - } - - case ssa.Op386UCOMISS, ssa.Op386UCOMISD: - push(s, v.Args[0]) - - // Compare. - p := s.Prog(x86.AFUCOMP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = s.SSEto387[v.Args[1].Reg()] + 1 - - // Save AX. - p = s.Prog(x86.AMOVL) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_AX - s.AddrScratch(&p.To) - - // Move status word into AX. - p = s.Prog(x86.AFSTSW) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_AX - - // Then move the flags we need to the integer flags. - s.Prog(x86.ASAHF) - - // Restore AX. - p = s.Prog(x86.AMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_AX - - case ssa.Op386SQRTSD: - push(s, v.Args[0]) - s.Prog(x86.AFSQRT) - popAndSave(s, v) - - case ssa.Op386FCHS: - push(s, v.Args[0]) - s.Prog(x86.AFCHS) - popAndSave(s, v) - - case ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD: - p := s.Prog(x86.AMOVL) - p.From.Type = obj.TYPE_REG - p.From.Reg = v.Args[0].Reg() - s.AddrScratch(&p.To) - p = s.Prog(x86.AFMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.Op386CVTTSD2SL, ssa.Op386CVTTSS2SL: - push(s, v.Args[0]) - - // Load control word which truncates (rounds towards zero). - installControlWord(s, gc.ControlWord64trunc, v.Reg()) - - // Now do the conversion. - p := s.Prog(x86.AFMOVLP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - s.AddrScratch(&p.To) - p = s.Prog(x86.AMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = v.Reg() - - // Restore control word. - restoreControlWord(s) - - case ssa.Op386CVTSS2SD: - // float32 -> float64 is a nop - push(s, v.Args[0]) - popAndSave(s, v) - - case ssa.Op386CVTSD2SS: - // Round to nearest float32. - push(s, v.Args[0]) - p := s.Prog(x86.AFMOVFP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - s.AddrScratch(&p.To) - p = s.Prog(x86.AFMOVF) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.OpLoadReg: - if !v.Type.IsFloat() { - ssaGenValue(s, v) - return - } - // Load+push the value we need. - p := s.Prog(loadPush(v.Type)) - gc.AddrAuto(&p.From, v.Args[0]) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - // Move the value to its assigned register. - popAndSave(s, v) - - case ssa.OpStoreReg: - if !v.Type.IsFloat() { - ssaGenValue(s, v) - return - } - push(s, v.Args[0]) - var op obj.As - switch v.Type.Size() { - case 4: - op = x86.AFMOVFP - case 8: - op = x86.AFMOVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - gc.AddrAuto(&p.To, v) - - case ssa.OpCopy: - if !v.Type.IsFloat() { - ssaGenValue(s, v) - return - } - push(s, v.Args[0]) - popAndSave(s, v) - - case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter: - flush387(s) // Calls must empty the FP stack. - fallthrough // then issue the call as normal - default: - ssaGenValue(s, v) - } -} - -// push pushes v onto the floating-point stack. v must be in a register. -func push(s *gc.SSAGenState, v *ssa.Value) { - p := s.Prog(x86.AFMOVD) - p.From.Type = obj.TYPE_REG - p.From.Reg = s.SSEto387[v.Reg()] - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 -} - -// popAndSave pops a value off of the floating-point stack and stores -// it in the register assigned to v. -func popAndSave(s *gc.SSAGenState, v *ssa.Value) { - r := v.Reg() - if _, ok := s.SSEto387[r]; ok { - // Pop value, write to correct register. - p := s.Prog(x86.AFMOVDP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = s.SSEto387[v.Reg()] + 1 - } else { - // Don't actually pop value. This 387 register is now the - // new home for the not-yet-assigned-a-home SSE register. - // Increase the register mapping of all other registers by one. - for rSSE, r387 := range s.SSEto387 { - s.SSEto387[rSSE] = r387 + 1 - } - s.SSEto387[r] = x86.REG_F0 - } -} - -// loadPush returns the opcode for load+push of the given type. -func loadPush(t *types.Type) obj.As { - if t.Size() == 4 { - return x86.AFMOVF - } - return x86.AFMOVD -} - -// flush387 removes all entries from the 387 floating-point stack. -func flush387(s *gc.SSAGenState) { - for k := range s.SSEto387 { - p := s.Prog(x86.AFMOVDP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - delete(s.SSEto387, k) - } -} - -func ssaGenBlock387(s *gc.SSAGenState, b, next *ssa.Block) { - // Empty the 387's FP stack before the block ends. - flush387(s) - - ssaGenBlock(s, b, next) -} - -// installControlWord saves the current floating-point control -// word and installs a new one loaded from cw. -// scratchReg must be an unused register. -// This call must be paired with restoreControlWord. -// Bytes 4-5 of the scratch space (s.AddrScratch) are used between -// this call and restoreControlWord. -func installControlWord(s *gc.SSAGenState, cw *obj.LSym, scratchReg int16) { - // Save current control word. - p := s.Prog(x86.AFSTCW) - s.AddrScratch(&p.To) - p.To.Offset += 4 - - // Materialize address of new control word. - // Note: this must be a seperate instruction to handle PIE correctly. - // See issue 41503. - p = s.Prog(x86.ALEAL) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = cw - p.To.Type = obj.TYPE_REG - p.To.Reg = scratchReg - - // Load replacement control word. - p = s.Prog(x86.AFLDCW) - p.From.Type = obj.TYPE_MEM - p.From.Reg = scratchReg -} -func restoreControlWord(s *gc.SSAGenState) { - p := s.Prog(x86.AFLDCW) - s.AddrScratch(&p.From) - p.From.Offset += 4 -} diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go index 56c6989d93..2d20b6a6d0 100644 --- a/src/cmd/compile/internal/x86/galign.go +++ b/src/cmd/compile/internal/x86/galign.go @@ -7,26 +7,13 @@ package x86 import ( "cmd/compile/internal/gc" "cmd/internal/obj/x86" - "cmd/internal/objabi" - "fmt" - "os" ) func Init(arch *gc.Arch) { arch.LinkArch = &x86.Link386 arch.REGSP = x86.REGSP - switch v := objabi.GO386; v { - case "387": - arch.Use387 = true - arch.SSAGenValue = ssaGenValue387 - arch.SSAGenBlock = ssaGenBlock387 - case "sse2": - arch.SSAGenValue = ssaGenValue - arch.SSAGenBlock = ssaGenBlock - default: - fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v) - gc.Exit(1) - } + arch.SSAGenValue = ssaGenValue + arch.SSAGenBlock = ssaGenBlock arch.MAXWIDTH = (1 << 32) - 1 arch.ZeroRange = zerorange diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go index c21ac32297..74a4570770 100644 --- a/src/cmd/compile/internal/x86/ssa.go +++ b/src/cmd/compile/internal/x86/ssa.go @@ -852,8 +852,6 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers gc.Warnl(v.Pos, "generated nil check") } - case ssa.Op386FCHS: - v.Fatalf("FCHS in non-387 mode") case ssa.OpClobber: p := s.Prog(x86.AMOVL) p.From.Type = obj.TYPE_CONST diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 3ac742fa55..5d62c1e8fa 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -30,7 +30,6 @@ var ( gohostos string goos string goarm string - go386 string gomips string gomips64 string goppc64 string @@ -142,16 +141,6 @@ func xinit() { } goarm = b - b = os.Getenv("GO386") - if b == "" { - if cansse2() { - b = "sse2" - } else { - b = "387" - } - } - go386 = b - b = os.Getenv("GOMIPS") if b == "" { b = "hardfloat" @@ -223,7 +212,6 @@ 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) @@ -1165,9 +1153,6 @@ 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 2744951597..67d1d72db4 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -41,7 +41,6 @@ func mkzversion(dir, file string) { // package objabi // // const defaultGOROOT = -// const defaultGO386 = // const defaultGOARM = // const defaultGOMIPS = // const defaultGOMIPS64 = @@ -70,7 +69,6 @@ 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/dist/cpuid_386.s b/src/cmd/dist/cpuid_386.s deleted file mode 100644 index 65fbb2dcb7..0000000000 --- a/src/cmd/dist/cpuid_386.s +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !gccgo - -TEXT ·cpuid(SB),$0-8 - MOVL ax+4(FP), AX - CPUID - MOVL info+0(FP), DI - MOVL AX, 0(DI) - MOVL BX, 4(DI) - MOVL CX, 8(DI) - MOVL DX, 12(DI) - RET - diff --git a/src/cmd/dist/cpuid_amd64.s b/src/cmd/dist/cpuid_amd64.s deleted file mode 100644 index ea0b9d4dc9..0000000000 --- a/src/cmd/dist/cpuid_amd64.s +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !gccgo - -TEXT ·cpuid(SB),$0-12 - MOVL ax+8(FP), AX - CPUID - MOVQ info+0(FP), DI - MOVL AX, 0(DI) - MOVL BX, 4(DI) - MOVL CX, 8(DI) - MOVL DX, 12(DI) - RET - diff --git a/src/cmd/dist/cpuid_default.s b/src/cmd/dist/cpuid_default.s deleted file mode 100644 index 6412a507a9..0000000000 --- a/src/cmd/dist/cpuid_default.s +++ /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. - -// +build !386,!amd64,!gccgo - -#include "textflag.h" - -TEXT ·cpuid(SB),NOSPLIT,$0-0 - RET diff --git a/src/cmd/dist/util_gc.go b/src/cmd/dist/util_gc.go index 698beef704..17a0e6fbb5 100644 --- a/src/cmd/dist/util_gc.go +++ b/src/cmd/dist/util_gc.go @@ -6,18 +6,6 @@ package main -func cpuid(info *[4]uint32, ax uint32) - -func cansse2() bool { - if gohostarch != "386" && gohostarch != "amd64" { - return false - } - - var info [4]uint32 - cpuid(&info, 1) - return info[3]&(1<<26) != 0 // SSE2 -} - // useVFPv1 tries to execute one VFPv1 instruction on ARM. // It will crash the current process if VFPv1 is missing. func useVFPv1() diff --git a/src/cmd/dist/util_gccgo.go b/src/cmd/dist/util_gccgo.go index f9f01dc048..dc897236fb 100644 --- a/src/cmd/dist/util_gccgo.go +++ b/src/cmd/dist/util_gccgo.go @@ -6,19 +6,6 @@ package main -/* -int supports_sse2() { -#if defined(__i386__) || defined(__x86_64__) - return __builtin_cpu_supports("sse2"); -#else - return 0; -#endif -} -*/ -import "C" - -func cansse2() bool { return C.supports_sse2() != 0 } - func useVFPv1() {} func useVFPv3() {} diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 4bc87008ff..500682ed02 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1853,9 +1853,6 @@ // GOARM // For GOARCH=arm, the ARM architecture for which to compile. // Valid values are 5, 6, 7. -// GO386 -// For GOARCH=386, the floating point instruction set. -// Valid values are 387, sse2. // 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 9bf1db73ef..ebbaf04115 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -244,7 +244,6 @@ 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)) @@ -268,8 +267,6 @@ 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/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index 7bd75f7305..ee0bb0d0b2 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -497,7 +497,10 @@ func lineToKey(line string) string { } // sortKeyValues sorts a sequence of lines by key. -// It differs from sort.Strings in that GO386= sorts after GO=. +// It differs from sort.Strings in that keys which are GOx where x is an ASCII +// character smaller than = sort after GO=. +// (There are no such keys currently. It used to matter for GO386 which was +// removed in Go 1.16.) func sortKeyValues(lines []string) { sort.Slice(lines, func(i, j int) bool { return lineToKey(lines[i]) < lineToKey(lines[j]) diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 0ae5fd7ca9..befa10a0e4 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -581,9 +581,6 @@ 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, the floating point instruction set. - Valid values are 387, sse2. GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 51fc2b588d..e68b322c7d 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -271,7 +271,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags) } - // GO386, GOARM, GOMIPS, etc. + // GOARM, GOMIPS, etc. key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) @@ -1175,7 +1175,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags) } - // GO386, GOARM, GOMIPS, etc. + // GOARM, GOMIPS, etc. key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index b81b73a022..cedb2d0a26 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -24,7 +24,6 @@ var ( GOROOT = envOr("GOROOT", defaultGOROOT) GOARCH = envOr("GOARCH", defaultGOARCH) GOOS = envOr("GOOS", defaultGOOS) - GO386 = envOr("GO386", defaultGO386) GOAMD64 = goamd64() GOARM = goarm() GOMIPS = gomips() @@ -136,6 +135,14 @@ 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 bdbe9df3e7..023429e441 100644 --- a/src/internal/cfg/cfg.go +++ b/src/internal/cfg/cfg.go @@ -32,7 +32,6 @@ const KnownEnv = ` FC GCCGO GO111MODULE - GO386 GOARCH GOARM GOBIN diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index ec87ec0c8a..0684eab973 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4265,24 +4265,6 @@ var gFloat32 float32 func TestConvertNaNs(t *testing.T) { const snan uint32 = 0x7f800001 - - // Test to see if a store followed by a load of a signaling NaN - // maintains the signaling bit. The only platform known to fail - // this test is 386,GO386=387. The real test below will always fail - // if the platform can't even store+load a float without mucking - // with the bits. - gFloat32 = math.Float32frombits(snan) - runtime.Gosched() // make sure we don't optimize the store/load away - r := math.Float32bits(gFloat32) - if r != snan { - // This should only happen on 386,GO386=387. We have no way to - // test for 387, so we just make sure we're at least on 386. - if runtime.GOARCH != "386" { - t.Errorf("store/load of sNaN not faithful") - } - t.Skip("skipping test, float store+load not faithful") - } - type myFloat32 float32 x := V(myFloat32(math.Float32frombits(snan))) y := x.Convert(TypeOf(float32(0))) diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index c2e14cdcd6..c5bfb0f207 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -190,40 +190,25 @@ func (l *layout) restore() { func gen386() { p("PUSHFL") - // Save general purpose registers. + // Assign stack offsets. var l = layout{sp: "SP"} for _, reg := range regNames386 { - if reg == "SP" || strings.HasPrefix(reg, "X") { + if reg == "SP" { continue } - l.add("MOVL", reg, 4) - } - - // Save the 387 state. - l.addSpecial( - "FSAVE %d(SP)\nFLDCW runtime·controlWord64(SB)", - "FRSTOR %d(SP)", - 108) - - // 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) + if strings.HasPrefix(reg, "X") { + l.add("MOVUPS", reg, 16) + } else { + l.add("MOVL", reg, 4) + } } - p("ADJSP $%d", lSSE.stack) + p("ADJSP $%d", l.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", -lSSE.stack) - + p("ADJSP $%d", -l.stack) p("POPFL") p("RET") } diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s index a00ac8f385..5c9b8ea224 100644 --- a/src/runtime/preempt_386.s +++ b/src/runtime/preempt_386.s @@ -5,7 +5,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHFL - ADJSP $264 + ADJSP $156 NOP SP MOVL AX, 0(SP) MOVL CX, 4(SP) @@ -14,32 +14,23 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVL BP, 16(SP) MOVL SI, 20(SP) MOVL DI, 24(SP) - FSAVE 28(SP) - FLDCW runtime·controlWord64(SB) - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse - MOVUPS X0, 136(SP) - MOVUPS X1, 152(SP) - MOVUPS X2, 168(SP) - MOVUPS X3, 184(SP) - MOVUPS X4, 200(SP) - MOVUPS X5, 216(SP) - MOVUPS X6, 232(SP) - MOVUPS X7, 248(SP) -nosse: + MOVUPS X0, 28(SP) + MOVUPS X1, 44(SP) + MOVUPS X2, 60(SP) + MOVUPS X3, 76(SP) + MOVUPS X4, 92(SP) + MOVUPS X5, 108(SP) + MOVUPS X6, 124(SP) + MOVUPS X7, 140(SP) CALL ·asyncPreempt2(SB) - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse2 - MOVUPS 248(SP), X7 - MOVUPS 232(SP), X6 - MOVUPS 216(SP), X5 - MOVUPS 200(SP), X4 - MOVUPS 184(SP), X3 - MOVUPS 168(SP), X2 - MOVUPS 152(SP), X1 - MOVUPS 136(SP), X0 -nosse2: - FRSTOR 28(SP) + MOVUPS 140(SP), X7 + MOVUPS 124(SP), X6 + MOVUPS 108(SP), X5 + MOVUPS 92(SP), X4 + MOVUPS 76(SP), X3 + MOVUPS 60(SP), X2 + MOVUPS 44(SP), X1 + MOVUPS 28(SP), X0 MOVL 24(SP), DI MOVL 20(SP), SI MOVL 16(SP), BP @@ -47,6 +38,6 @@ nosse2: MOVL 8(SP), DX MOVL 4(SP), CX MOVL 0(SP), AX - ADJSP $-264 + ADJSP $-156 POPFL RET diff --git a/src/runtime/vlrt.go b/src/runtime/vlrt.go index 38e0b32801..996c0611fd 100644 --- a/src/runtime/vlrt.go +++ b/src/runtime/vlrt.go @@ -263,7 +263,7 @@ func slowdodiv(n, d uint64) (q, r uint64) { return q, n } -// Floating point control word values for GOARCH=386 GO386=387. +// Floating point control word values. // Bits 0-5 are bits to disable floating-point exceptions. // Bits 8-9 are the precision control: // 0 = single precision a.k.a. float32 @@ -273,6 +273,5 @@ func slowdodiv(n, d uint64) (q, r uint64) { // 3 = round toward zero var ( controlWord64 uint16 = 0x3f + 2<<8 + 0<<10 - controlWord32 = 0x3f + 0<<8 + 0<<10 - controlWord64trunc = 0x3f + 2<<8 + 3<<10 + controlWord64trunc uint16 = 0x3f + 2<<8 + 3<<10 ) diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go index 0bdb66a376..30f39a8da1 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/sse2:`MULSS\s4\([A-Z]+\),\sX[0-9]+` + // 386:`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/sse2:`DIVSD\s8\([A-Z]+\),\sX[0-9]+` + // 386:`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/sse2:`DIVSS\s8\([A-Z]+\),\sX[0-9]+` + // 386:`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 3fae1a327c..d115800a67 100644 --- a/test/codegen/floats.go +++ b/test/codegen/floats.go @@ -6,8 +6,6 @@ package codegen -import "math" - // This file contains codegen tests related to arithmetic // simplifications and optimizations on float types. // For codegen tests on integer types, see arithmetic.go. @@ -17,8 +15,7 @@ import "math" // --------------------- // func Mul2(f float64) float64 { - // 386/sse2:"ADDSD",-"MULSD" - // 386/387:"FADDDP",-"FMULDP" + // 386:"ADDSD",-"MULSD" // amd64:"ADDSD",-"MULSD" // arm/7:"ADDD",-"MULD" // arm64:"FADDD",-"FMULD" @@ -28,8 +25,7 @@ func Mul2(f float64) float64 { } func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { - // 386/sse2:"MULSD",-"DIVSD" - // 386/387:"FMULDP",-"FDIVDP" + // 386:"MULSD",-"DIVSD" // amd64:"MULSD",-"DIVSD" // arm/7:"MULD",-"DIVD" // arm64:"FMULD",-"FDIVD" @@ -37,8 +33,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { // ppc64le:"FMUL",-"FDIV" x := f1 / 16.0 - // 386/sse2:"MULSD",-"DIVSD" - // 386/387:"FMULDP",-"FDIVDP" + // 386:"MULSD",-"DIVSD" // amd64:"MULSD",-"DIVSD" // arm/7:"MULD",-"DIVD" // arm64:"FMULD",-"FDIVD" @@ -46,8 +41,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { // ppc64le:"FMUL",-"FDIVD" y := f2 / 0.125 - // 386/sse2:"ADDSD",-"DIVSD",-"MULSD" - // 386/387:"FADDDP",-"FDIVDP",-"FMULDP" + // 386:"ADDSD",-"DIVSD",-"MULSD" // amd64:"ADDSD",-"DIVSD",-"MULSD" // arm/7:"ADDD",-"MULD",-"DIVD" // arm64:"FADDD",-"FMULD",-"FDIVD" @@ -58,11 +52,6 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { return x, y, z } -func getPi() float64 { - // 386/387:"FLDPI" - return math.Pi -} - func indexLoad(b0 []float32, b1 float32, idx int) float32 { // arm64:`FMOVS\s\(R[0-9]+\)\(R[0-9]+\),\sF[0-9]+` return b0[idx] * b1 diff --git a/test/codegen/math.go b/test/codegen/math.go index 1ebfda0405..fe678eea23 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/387:"FSQRT" 386/sse2:"SQRTSD" + // 386:"SQRTSD" // arm64:"FSQRTD" // arm/7:"SQRTD" // mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD" diff --git a/test/codegen/memops.go b/test/codegen/memops.go index a234283146..4b003ad861 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/sse2: `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: `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/sse2: `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: `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/sse2: `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: `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/sse2: `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: `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/sse2: `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: `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/sse2: `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: `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/sse2: `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: `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/sse2: `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: `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 95b94b7277..77710fd89a 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": {"GO386", "387", "sse2"}, + "386": {}, "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/386/sse2). +// A build environment triplet separated by slashes (eg: linux/arm/7). // 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/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"} +// For instance, "linux/arm/7".Environ() returns {"GOOS=linux", "GOARCH=arm", "GOARM=7"} 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/386/sse2" + case archspec[2] != "": // 3 components: "linux/arm/7" os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:] - case archspec[1] != "": // 2 components: "386/sse2" + case archspec[1] != "": // 2 components: "arm/7" os, arch, subarch = "linux", archspec[0], archspec[1][1:] - default: // 1 component: "386" + default: // 1 component: "arm" os, arch, subarch = "linux", archspec[0], "" if arch == "wasm" { os = "js" -- cgit v1.3 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 (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 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 (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 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(-) (limited to 'src/cmd/dist') 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.3 From f46a5b1e4559191363dbd4f510105dd31ae97aaa Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sun, 11 Oct 2020 15:48:22 -0400 Subject: cmd/link: support PIE internal linking on darwin/amd64 This CL adds support of PIE internal linking on darwin/amd64. This is also preparation for supporting internal linking on darwin/arm64 (macOS), which requires PIE for everything. Updates #38485. Change-Id: I2ed58583dcc102f5e0521982491fc7ba6f2754ed Reviewed-on: https://go-review.googlesource.com/c/go/+/261642 Trust: Cherry Zhang Reviewed-by: Than McIntosh --- misc/cgo/test/issue4029.c | 1 + misc/cgo/test/issue4029.go | 4 + misc/cgo/test/issue4029w.go | 2 +- src/cmd/dist/test.go | 17 +- src/cmd/internal/sys/supported.go | 3 +- src/cmd/link/internal/amd64/asm.go | 65 +++----- src/cmd/link/internal/ld/config.go | 1 + src/cmd/link/internal/ld/lib.go | 6 + src/cmd/link/internal/ld/macho.go | 226 +++++++++++++++++++++++++- src/cmd/link/internal/loader/symbolbuilder.go | 18 ++ 10 files changed, 289 insertions(+), 54 deletions(-) (limited to 'src/cmd/dist') diff --git a/misc/cgo/test/issue4029.c b/misc/cgo/test/issue4029.c index 30646ade02..e6a777fe64 100644 --- a/misc/cgo/test/issue4029.c +++ b/misc/cgo/test/issue4029.c @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // +build !windows,!static +// +build !darwin !internal_pie #include #include diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go index 1bf029d760..8602ce19e2 100644 --- a/misc/cgo/test/issue4029.go +++ b/misc/cgo/test/issue4029.go @@ -3,6 +3,10 @@ // license that can be found in the LICENSE file. // +build !windows,!static +// +build !darwin !internal_pie + +// Excluded in darwin internal linking PIE mode, as dynamic export is not +// supported. package cgotest diff --git a/misc/cgo/test/issue4029w.go b/misc/cgo/test/issue4029w.go index eee33f7010..de0cf2138a 100644 --- a/misc/cgo/test/issue4029w.go +++ b/misc/cgo/test/issue4029w.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build windows static +// +build windows static darwin,internal_pie package cgotest diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 03e6866d62..94b81613a9 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -964,10 +964,10 @@ func (t *tester) internalLink() bool { func (t *tester) internalLinkPIE() bool { switch goos + "-" + goarch { - case "linux-amd64", "linux-arm64", - "android-arm64": - return true - case "windows-amd64", "windows-386", "windows-arm": + case "darwin-amd64", + "linux-amd64", "linux-arm64", + "android-arm64", + "windows-amd64", "windows-386", "windows-arm": return true } return false @@ -1100,6 +1100,13 @@ func (t *tester) cgoTest(dt *distTest) error { cmd = t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s") + if t.supportedBuildmode("pie") { + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie") + if t.internalLink() && t.internalLinkPIE() { + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie") + } + } + case "aix-ppc64", "android-arm", "android-arm64", "dragonfly-amd64", @@ -1151,7 +1158,7 @@ func (t *tester) cgoTest(dt *distTest) error { if t.supportedBuildmode("pie") { t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie") if t.internalLink() && t.internalLinkPIE() { - t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal") + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie") } t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-buildmode=pie") t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-buildmode=pie") diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index 55709f3915..07be998035 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -118,7 +118,8 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { func InternalLinkPIESupported(goos, goarch string) bool { switch goos + "/" + goarch { - case "linux/amd64", "linux/arm64", + case "darwin/amd64", + "linux/amd64", "linux/arm64", "android/arm64", "windows-amd64", "windows-386", "windows-arm": return true diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index e5a6ef51b0..3658ac0be0 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -76,9 +76,9 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade targType = ldr.SymType(targ) } - switch r.Type() { + switch rt := r.Type(); rt { default: - if r.Type() >= objabi.ElfRelocOffset { + if rt >= objabi.ElfRelocOffset { ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) return false } @@ -167,13 +167,24 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: - // TODO: What is the difference between all these? su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_ADDR) if targType == sym.SDYNIMPORT { ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) } + if target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + if rt == objabi.MachoRelocOffset+ld.MACHO_X86_64_RELOC_UNSIGNED*2 { + break + } else { + // MACHO_X86_64_RELOC_SIGNED or MACHO_X86_64_RELOC_BRANCH + // Can this happen? The object is expected to be PIC. + ldr.Errorf(s, "unsupported relocation for PIE: %v", rt) + } + } return true case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: @@ -223,7 +234,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade if targType != sym.SDYNIMPORT { ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) } - ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT)) + ld.AddGotSym(target, ldr, syms, targ, 0) su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_PCREL) su.SetRelocSym(rIdx, syms.GOT) @@ -355,28 +366,15 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade return true } - if target.IsDarwin() && ldr.SymSize(s) == int64(target.Arch.PtrSize) && r.Off() == 0 { + if target.IsDarwin() { // Mach-O relocations are a royal pain to lay out. - // They use a compact stateful bytecode representation - // that is too much bother to deal with. - // Instead, interpret the C declaration - // void *_Cvar_stderr = &stderr; - // as making _Cvar_stderr the name of a GOT entry - // for stderr. This is separate from the usual GOT entry, - // just in case the C code assigns to the variable, - // and of course it only works for single pointers, - // but we only need to support cgo and that's all it needs. - ld.Adddynsym(ldr, target, syms, targ) - - got := ldr.MakeSymbolUpdater(syms.GOT) - su := ldr.MakeSymbolUpdater(s) - su.SetType(got.Type()) - got.AddInteriorSym(s) - su.SetValue(got.Size()) - got.AddUint64(target.Arch, 0) - leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT) - leg.AddUint32(target.Arch, uint32(ldr.SymDynid(targ))) - su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym + // They use a compact stateful bytecode representation. + // Here we record what are needed and encode them later. + ld.MachoAddRebase(s, int64(r.Off())) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). return true } } @@ -627,26 +625,16 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade ldr.SetPlt(s, int32(plt.Size()-16)) } else if target.IsDarwin() { - // To do lazy symbol lookup right, we're supposed - // to tell the dynamic loader which library each - // symbol comes from and format the link info - // section just so. I'm too lazy (ha!) to do that - // so for now we'll just use non-lazy pointers, - // which don't need to be told which library to use. - // - // https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html - // has details about what we're avoiding. - - ld.AddGotSym(target, ldr, syms, s, uint32(elf.R_X86_64_GLOB_DAT)) - plt := ldr.MakeSymbolUpdater(syms.PLT) + ld.AddGotSym(target, ldr, syms, s, 0) sDynid := ldr.SymDynid(s) lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT) lep.AddUint32(target.Arch, uint32(sDynid)) - // jmpq *got+size(IP) + plt := ldr.MakeSymbolUpdater(syms.PLT) ldr.SetPlt(s, int32(plt.Size())) + // jmpq *got+size(IP) plt.AddUint8(0xff) plt.AddUint8(0x25) plt.AddPCRelPlus(target.Arch, syms.GOT, int64(ldr.SymGot(s))) @@ -654,6 +642,7 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade ldr.Errorf(s, "addpltsym: unsupported binary format") } } + func tlsIEtoLE(P []byte, off, size int) { // Transform the PC-relative instruction into a constant load. // That is, diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index aaf74b58de..c680d11c1d 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -222,6 +222,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { switch objabi.GOOS + "/" + objabi.GOARCH { case "linux/amd64", "linux/arm64", "android/arm64": case "windows/386", "windows/amd64", "windows/arm": + case "darwin/amd64": default: // Internal linking does not support TLS_IE. return true, "buildmode=pie" diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 5fe028d321..8d04973fcf 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2526,6 +2526,12 @@ func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, } else if target.IsDarwin() { leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT) leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s))) + if target.IsPIE() && target.IsInternal() { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation. + // Here we record what are needed and encode them later. + MachoAddBind(int64(ldr.SymGot(s)), s) + } } else { ldr.Errorf(s, "addgotsym: unsupported binary format") } diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index eab6537c75..a19a4afd9a 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -118,6 +118,8 @@ const ( MH_EXECUTE = 0x2 MH_NOUNDEFS = 0x1 + MH_DYLDLINK = 0x4 + MH_PIE = 0x200000 ) const ( @@ -193,6 +195,56 @@ const ( PLATFORM_BRIDGEOS MachoPlatform = 5 ) +// rebase table opcode +const ( + REBASE_TYPE_POINTER = 1 + REBASE_TYPE_TEXT_ABSOLUTE32 = 2 + REBASE_TYPE_TEXT_PCREL32 = 3 + + REBASE_OPCODE_MASK = 0xF0 + REBASE_IMMEDIATE_MASK = 0x0F + REBASE_OPCODE_DONE = 0x00 + REBASE_OPCODE_SET_TYPE_IMM = 0x10 + REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20 + REBASE_OPCODE_ADD_ADDR_ULEB = 0x30 + REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40 + REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60 + REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80 +) + +// bind table opcode +const ( + BIND_TYPE_POINTER = 1 + BIND_TYPE_TEXT_ABSOLUTE32 = 2 + BIND_TYPE_TEXT_PCREL32 = 3 + + BIND_SPECIAL_DYLIB_SELF = 0 + BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1 + BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2 + BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3 + + BIND_OPCODE_MASK = 0xF0 + BIND_IMMEDIATE_MASK = 0x0F + BIND_OPCODE_DONE = 0x00 + BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10 + BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20 + BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30 + BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40 + BIND_OPCODE_SET_TYPE_IMM = 0x50 + BIND_OPCODE_SET_ADDEND_SLEB = 0x60 + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70 + BIND_OPCODE_ADD_ADDR_ULEB = 0x80 + BIND_OPCODE_DO_BIND = 0x90 + BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0 + BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0 + BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0 + BIND_OPCODE_THREADED = 0xD0 + BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00 + BIND_SUBOPCODE_THREADED_APPLY = 0x01 +) + // Mach-O file writing // https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html @@ -279,7 +331,7 @@ var dylib []string var linkoff int64 -func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { +func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { o1 := out.Offset() loadsize := 4 * 4 * ndebug @@ -308,11 +360,14 @@ func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { } out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) out.Write32(uint32(loadsize)) + flags := uint32(0) if nkind[SymKindUndef] == 0 { - out.Write32(MH_NOUNDEFS) /* flags - no undefines */ - } else { - out.Write32(0) /* flags */ + flags |= MH_NOUNDEFS + } + if ctxt.IsPIE() && linkmode == LinkInternal { + flags |= MH_PIE | MH_DYLDLINK } + out.Write32(flags) /* flags */ if arch.PtrSize == 8 { out.Write32(0) /* reserved */ } @@ -712,15 +767,17 @@ func asmbMacho(ctxt *Link) { s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) s4 := ldr.SymSize(ldr.Lookup(".machosymstr", 0)) + s5 := ldr.SymSize(ldr.Lookup(".machorebase", 0)) + s6 := ldr.SymSize(ldr.Lookup(".machobind", 0)) if ctxt.LinkMode != LinkExternal { ms := newMachoSeg("__LINKEDIT", 0) ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound))) - ms.vsize = uint64(s1) + uint64(s2) + uint64(s3) + uint64(s4) + ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6) ms.fileoffset = uint64(linkoff) ms.filesize = ms.vsize - ms.prot1 = 7 - ms.prot2 = 3 + ms.prot1 = 1 + ms.prot2 = 1 } ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4) @@ -745,9 +802,23 @@ func asmbMacho(ctxt *Link) { stringtouint32(ml.data[4:], lib) } } + + if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() { + ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10) + ml.data[0] = uint32(linkoff + s1 + s2 + s3 + s4) // rebase off + ml.data[1] = uint32(s5) // rebase size + ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) // bind off + ml.data[3] = uint32(s6) // bind size + ml.data[4] = 0 // weak bind off + ml.data[5] = 0 // weak bind size + ml.data[6] = 0 // lazy bind off + ml.data[7] = 0 // lazy bind size + ml.data[8] = 0 // export + ml.data[9] = 0 // export size + } } - a := machowrite(ctxt.Arch, ctxt.Out, ctxt.LinkMode) + a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode) if int32(a) > HEADR { Exitf("HEADR too small: %d > %d", a, HEADR) } @@ -989,6 +1060,8 @@ func machodysymtab(ctxt *Link) { func doMachoLink(ctxt *Link) int64 { machosymtab(ctxt) + machoDyldInfo(ctxt) + ldr := ctxt.loader // write data that will be linkedit section @@ -996,6 +1069,8 @@ func doMachoLink(ctxt *Link) int64 { s2 := ctxt.ArchSyms.LinkEditPLT s3 := ctxt.ArchSyms.LinkEditGOT s4 := ldr.Lookup(".machosymstr", 0) + s5 := ldr.Lookup(".machorebase", 0) + s6 := ldr.Lookup(".machobind", 0) // Force the linkedit section to end on a 16-byte // boundary. This allows pure (non-cgo) Go binaries @@ -1019,7 +1094,7 @@ func doMachoLink(ctxt *Link) int64 { s4b.AddUint8(0) } - size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4)) + size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6)) if size > 0 { linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound)) @@ -1029,6 +1104,8 @@ func doMachoLink(ctxt *Link) int64 { ctxt.Out.Write(ldr.Data(s2)) ctxt.Out.Write(ldr.Data(s3)) ctxt.Out.Write(ldr.Data(s4)) + ctxt.Out.Write(ldr.Data(s5)) + ctxt.Out.Write(ldr.Data(s6)) } return Rnd(int64(size), int64(*FlagRound)) @@ -1172,3 +1249,134 @@ func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) { } return nil, nil } + +// A rebase entry tells the dynamic linker the data at sym+off needs to be +// relocated when the in-memory image moves. (This is somewhat like, say, +// ELF R_X86_64_RELATIVE). +// For now, the only kind of entry we support is that the data is an absolute +// address. That seems all we need. +// In the binary it uses a compact stateful bytecode encoding. So we record +// entries as we go and build the table at the end. +type machoRebaseRecord struct { + sym loader.Sym + off int64 +} + +var machorebase []machoRebaseRecord + +func MachoAddRebase(s loader.Sym, off int64) { + machorebase = append(machorebase, machoRebaseRecord{s, off}) +} + +// A bind entry tells the dynamic linker the data at GOT+off should be bound +// to the address of the target symbol, which is a dynamic import. +// For now, the only kind of entry we support is that the data is an absolute +// address, and the source symbol is always the GOT. That seems all we need. +// In the binary it uses a compact stateful bytecode encoding. So we record +// entries as we go and build the table at the end. +type machoBindRecord struct { + off int64 + targ loader.Sym +} + +var machobind []machoBindRecord + +func MachoAddBind(off int64, targ loader.Sym) { + machobind = append(machobind, machoBindRecord{off, targ}) +} + +// Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command. +// See mach-o/loader.h, struct dyld_info_command, for the encoding. +// e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h +func machoDyldInfo(ctxt *Link) { + ldr := ctxt.loader + rebase := ldr.CreateSymForUpdate(".machorebase", 0) + bind := ldr.CreateSymForUpdate(".machobind", 0) + + if !(ctxt.IsPIE() && ctxt.IsInternal()) { + return + } + + segId := func(seg *sym.Segment) uint8 { + switch seg { + case &Segtext: + return 1 + case &Segrelrodata: + return 2 + case &Segdata: + if Segrelrodata.Length > 0 { + return 3 + } + return 2 + } + panic("unknown segment") + } + + dylibId := func(s loader.Sym) int { + slib := ldr.SymDynimplib(s) + for i, lib := range dylib { + if lib == slib { + return i + 1 + } + } + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from + } + + // Rebase table. + // TODO: use more compact encoding. The encoding is stateful, and + // we can use delta encoding. + rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER) + for _, r := range machorebase { + seg := ldr.SymSect(r.sym).Seg + off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr + rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) + rebase.AddUleb(off) + + rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1) + } + rebase.AddUint8(REBASE_OPCODE_DONE) + sz := Rnd(rebase.Size(), 8) + rebase.Grow(sz) + rebase.SetSize(sz) + + // Bind table. + // TODO: compact encoding, as above. + // TODO: lazy binding? + got := ctxt.GOT + seg := ldr.SymSect(got).Seg + gotAddr := ldr.SymValue(got) + bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER) + for _, r := range machobind { + off := uint64(gotAddr+r.off) - seg.Vaddr + bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) + bind.AddUleb(off) + + d := dylibId(r.targ) + if d > 0 && d < 128 { + bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf) + } else if d >= 128 { + bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) + bind.AddUleb(uint64(d)) + } else { // d <= 0 + bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf) + } + + bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM) + // target symbol name as a C string, with _ prefix + bind.AddUint8('_') + bind.Addstring(ldr.SymExtname(r.targ)) + + bind.AddUint8(BIND_OPCODE_DO_BIND) + } + bind.AddUint8(BIND_OPCODE_DONE) + sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink + bind.Grow(sz) + bind.SetSize(sz) + + // TODO: export table. + // The symbols names are encoded as a trie. I'm really too lazy to do that + // for now. + // Without it, the symbols are not dynamically exported, so they cannot be + // e.g. dlsym'd. But internal linking is not the default in that case, so + // it is fine. +} diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go index c0c723d7f0..5d37da8ac6 100644 --- a/src/cmd/link/internal/loader/symbolbuilder.go +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -420,3 +420,21 @@ func (sb *SymbolBuilder) MakeWritable() { sb.l.SetAttrReadOnly(sb.symIdx, false) } } + +func (sb *SymbolBuilder) AddUleb(v uint64) { + if v < 128 { // common case: 1 byte + sb.AddUint8(uint8(v)) + return + } + for { + c := uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + sb.AddUint8(c) + if c&0x80 == 0 { + break + } + } +} -- cgit v1.3 From 748c0d87e23eaae0ba133d2f613b82c009ac8cb4 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Tue, 13 Oct 2020 20:44:04 -0400 Subject: cmd/dist: only build ios_exec wrapper for ios Not for darwin/arm64. Updates #38485. Change-Id: I08a5f00fd77f20c9c483755a36755a63cf10aa1a Reviewed-on: https://go-review.googlesource.com/c/go/+/262558 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Austin Clements --- src/cmd/dist/build.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 69a66abd2d..398ed6bce1 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1462,8 +1462,8 @@ func wrapperPathFor(goos, goarch string) string { if gohostos != "android" { return pathf("%s/misc/android/go_android_exec.go", goroot) } - case (goos == "darwin" || goos == "ios") && goarch == "arm64": - if gohostos != "darwin" || gohostarch != "arm64" { + case goos == "ios": + if gohostos != "ios" { return pathf("%s/misc/ios/go_ios_exec.go", goroot) } } -- cgit v1.3 From 15eaa870e14195c17dbb4be0d11bc40dba94ff22 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Fri, 14 Aug 2020 00:13:28 +1000 Subject: cmd/link: add support for external linking on linux/riscv64 Fixes #36739 Change-Id: Id7573b343786360c72524f9f27d2a8f08d379cf3 Reviewed-on: https://go-review.googlesource.com/c/go/+/243517 Trust: Joel Sing Reviewed-by: Cherry Zhang Reviewed-by: Than McIntosh Run-TryBot: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/dist/test.go | 2 +- src/cmd/link/internal/ld/config.go | 4 +- src/cmd/link/internal/ld/elf.go | 3 + src/cmd/link/internal/ld/lib.go | 6 +- src/cmd/link/internal/ld/main.go | 8 ++ src/cmd/link/internal/loader/loader.go | 5 ++ src/cmd/link/internal/riscv64/asm.go | 148 +++++++++++++++++++++++++++++++-- src/cmd/link/internal/riscv64/obj.go | 3 + 8 files changed, 169 insertions(+), 10 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 94b81613a9..bcb12f29fb 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -921,7 +921,7 @@ func (t *tester) extLink() bool { "darwin-amd64", "darwin-arm64", "dragonfly-amd64", "freebsd-386", "freebsd-amd64", "freebsd-arm", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-riscv64", "linux-s390x", "netbsd-386", "netbsd-amd64", "openbsd-386", "openbsd-amd64", "windows-386", "windows-amd64": diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index f55e4fc027..834c87d06b 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -202,7 +202,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { // Internally linking cgo is incomplete on some architectures. // https://golang.org/issue/14449 // https://golang.org/issue/21961 - if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64) { + if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) { return true, objabi.GOARCH + " does not support internal cgo" } if iscgo && objabi.GOOS == "android" { @@ -285,8 +285,6 @@ func determineLinkMode(ctxt *Link) { } case LinkExternal: switch { - case objabi.GOARCH == "riscv64": - Exitf("external linking not supported for %s/riscv64", objabi.GOOS) case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix": Exitf("external linking not supported for %s/ppc64", objabi.GOOS) } diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index f5a2f899fc..f44e16583d 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -506,6 +506,9 @@ func Elfinit(ctxt *Link) { if ctxt.Arch.Family == sys.MIPS64 { ehdr.flags = 0x20000004 /* MIPS 3 CPIC */ } + if ctxt.Arch.Family == sys.RISCV64 { + ehdr.flags = 0x4 /* RISCV Float ABI Double */ + } elf64 = true ehdr.phoff = ELF64HDRSIZE /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */ diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 8d04973fcf..a68725bef9 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -247,12 +247,16 @@ type Arch struct { Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1. Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) - Gentext func(*Link, *loader.Loader) + Gentext func(*Link, *loader.Loader) // Generate text before addressing has been performed. Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1. PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + // Generate additional symbols for the native symbol table just prior to + // code generation. + GenSymsLate func(*Link, *loader.Loader) + // TLSIEtoLE converts a TLS Initial Executable relocation to // a TLS Local Executable relocation. // diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 3f7370b636..5ae57d1992 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -341,6 +341,14 @@ func Main(arch *sys.Arch, theArch Arch) { }(f, s) } wg.Wait() + + // Generate additional symbols for the native symbol table just prior + // to code generation. + bench.Start("GenSymsLate") + if thearch.GenSymsLate != nil { + thearch.GenSymsLate(ctxt, ctxt.loader) + } + bench.Start("Asmb2") asmb2(ctxt) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 19d8d98b1e..47cac0441b 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1791,6 +1791,11 @@ func (l *Loader) SortSub(s Sym) Sym { return sl[0].s } +// SortSyms sorts a list of symbols by their value. +func (l *Loader) SortSyms(ss []Sym) { + sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) }) +} + // Insure that reachable bitmap and its siblings have enough size. func (l *Loader) growAttrBitmaps(reqLen int) { if reqLen > l.attrReachable.Len() { diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go index 1236145fb1..66c47c69f8 100644 --- a/src/cmd/link/internal/riscv64/asm.go +++ b/src/cmd/link/internal/riscv64/asm.go @@ -11,20 +11,138 @@ import ( "cmd/link/internal/ld" "cmd/link/internal/loader" "cmd/link/internal/sym" + "debug/elf" "fmt" "log" + "sort" ) +// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils. +const fakeLabelName = ".L0 " + func gentext(ctxt *ld.Link, ldr *loader.Loader) { } +func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) { + if ctxt.LinkMode != ld.LinkExternal { + return + } + + // Generate a local text symbol for each relocation target, as the + // R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it. + if ctxt.Textp == nil { + log.Fatal("genSymsLate called before Textp has been assigned") + } + var hi20Syms []loader.Sym + for _, s := range ctxt.Textp { + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE { + continue + } + if r.Off() == 0 && ldr.SymType(s) == sym.STEXT { + // Use the symbol for the function instead of creating + // an overlapping symbol. + continue + } + + // TODO(jsing): Consider generating ELF symbols without needing + // loader symbols, in order to reduce memory consumption. This + // would require changes to genelfsym so that it called + // putelfsym and putelfsyment as appropriate. + sb := ldr.MakeSymbolBuilder(fakeLabelName) + sb.SetType(sym.STEXT) + sb.SetValue(ldr.SymValue(s) + int64(r.Off())) + sb.SetLocal(true) + sb.SetReachable(true) + sb.SetVisibilityHidden(true) + sb.SetSect(ldr.SymSect(s)) + if outer := ldr.OuterSym(s); outer != 0 { + ldr.AddInteriorSym(outer, sb.Sym()) + } + hi20Syms = append(hi20Syms, sb.Sym()) + } + } + ctxt.Textp = append(ctxt.Textp, hi20Syms...) + ldr.SortSyms(ctxt.Textp) +} + +func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym { + idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val }) + if idx >= len(ctxt.Textp) { + return 0 + } + if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT { + return s + } + return 0 +} + func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { - log.Fatalf("elfreloc1") - return false + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + switch r.Type { + case objabi.R_ADDR, objabi.R_DWARFSECREF: + out.Write64(uint64(sectoff)) + switch r.Size { + case 4: + out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32) + case 8: + out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32) + default: + ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type) + return false + } + out.Write64(uint64(r.Xadd)) + + case objabi.R_CALLRISCV: + // Call relocations are currently handled via R_RISCV_PCREL_ITYPE. + // TODO(jsing): Consider generating elf.R_RISCV_CALL instead of a + // HI20/LO12_I pair. + + case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE: + // Find the text symbol for the AUIPC instruction targeted + // by this relocation. + relocs := ldr.Relocs(s) + offset := int64(relocs.At(ri).Off()) + hi20Sym := findHI20Symbol(ctxt, ldr, ldr.SymValue(s)+offset) + if hi20Sym == 0 { + ld.Errorf(nil, "failed to find text symbol for HI20 relocation at %d (%x)", sectoff, ldr.SymValue(s)+offset) + return false + } + hi20ElfSym := ld.ElfSymForReloc(ctxt, hi20Sym) + + // Emit two relocations - a R_RISCV_PCREL_HI20 relocation and a + // corresponding R_RISCV_PCREL_LO12_I or R_RISCV_PCREL_LO12_S relocation. + // Note that the LO12 relocation must point to a target that has a valid + // HI20 PC-relative relocation text symbol, which in turn points to the + // given symbol. For further details see the ELF specification for RISC-V: + // + // https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses + // + var hiRel, loRel elf.R_RISCV + switch r.Type { + case objabi.R_RISCV_PCREL_ITYPE: + hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I + case objabi.R_RISCV_PCREL_STYPE: + hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S + } + out.Write64(uint64(sectoff)) + out.Write64(uint64(hiRel) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(loRel) | uint64(hi20ElfSym)<<32) + out.Write64(uint64(0)) + + default: + return false + } + + return true } func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { - log.Fatalf("elfsetuplt") + log.Fatalf("elfsetupplt") } func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { @@ -33,8 +151,20 @@ func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtRe } func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { - rs := r.Sym() - rs = ldr.ResolveABIAlias(rs) + if target.IsExternal() { + switch r.Type() { + case objabi.R_CALLRISCV: + return val, 0, true + + case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE: + return val, 2, true + } + + return val, 0, false + } + + rs := ldr.ResolveABIAlias(r.Sym()) + switch r.Type() { case objabi.R_CALLRISCV: // Nothing to do. @@ -89,3 +219,11 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant log.Fatalf("archrelocvariant") return -1 } + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + switch r.Type() { + case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE: + return ld.ExtrelocViaOuterSym(ldr, r, s), true + } + return loader.ExtReloc{}, false +} diff --git a/src/cmd/link/internal/riscv64/obj.go b/src/cmd/link/internal/riscv64/obj.go index e66d3cd856..917324d922 100644 --- a/src/cmd/link/internal/riscv64/obj.go +++ b/src/cmd/link/internal/riscv64/obj.go @@ -23,9 +23,12 @@ func Init() (*sys.Arch, ld.Arch) { Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, + Extreloc: extreloc, Elfreloc1: elfreloc1, + ElfrelocSize: 24, Elfsetupplt: elfsetupplt, Gentext: gentext, + GenSymsLate: genSymsLate, Machoreloc1: machoreloc1, Linuxdynld: "/lib/ld.so.1", -- cgit v1.3 From 9499a2e10840e3878bff06e7f9a3bdaa56c5ca45 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Wed, 26 Aug 2020 03:18:30 +1000 Subject: cmd/dist: detect gohostarch for openbsd/mips64 On OpenBSD/octeon `uname -m` returns 'octeon' and we need `uname -p` to determine the processor type (mips64). Update #40995 Change-Id: I37c53ca2c372ab958fff17917d82d41f4b0a0393 Reviewed-on: https://go-review.googlesource.com/c/go/+/250583 Trust: Joel Sing Run-TryBot: Joel Sing TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/cmd/dist/main.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go index 37fc522356..37de1acc31 100644 --- a/src/cmd/dist/main.go +++ b/src/cmd/dist/main.go @@ -133,6 +133,10 @@ func main() { if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM64_") { gohostarch = "arm64" } + case gohostos == "openbsd": + if strings.Contains(run("", CheckExit, "uname", "-p"), "mips64") { + gohostarch = "mips64" + } default: fatalf("unknown architecture: %s", out) } -- cgit v1.3 From 4d1cecdee8360ef12a817c124d7a04c9d29741c3 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 14 Oct 2020 08:06:54 -0400 Subject: cmd/dist,cmd/go: broaden use of asm macro GOEXPERIMENT_REGABI This extends a change made in https://golang.org/cl/252258 to the go command (to define an asm macro when GOEXPERIMENT=regabi is in effect); we need this same macro during the bootstrap build in order to build the runtime correctly. In addition, expand the set of packages where the macro is applied to {runtime, reflect, syscall, runtime/internal/*}, and move the logic for deciding when something is a "runtime package" out of the assembler and into cmd/{go,dist}, introducing a new assembler command line flag instead. Updates #27539, #40724. Change-Id: Ifcc7f029f56873584de1e543c55b0d3e54ad6c49 Reviewed-on: https://go-review.googlesource.com/c/go/+/262317 Trust: Than McIntosh Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Austin Clements Reviewed-by: Cherry Zhang --- src/cmd/asm/internal/flags/flags.go | 19 ++++++++++--------- src/cmd/asm/main.go | 4 ++-- src/cmd/dist/build.go | 35 +++++++++++++++++++++++++++++++++++ src/cmd/go/internal/work/gc.go | 22 ++++++++++++++-------- 4 files changed, 61 insertions(+), 19 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index 64024cc97d..0765439959 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -15,15 +15,16 @@ import ( ) var ( - Debug = flag.Bool("debug", false, "dump instructions as they are parsed") - OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument") - TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths") - Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library") - Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries") - AllErrors = flag.Bool("e", false, "no limit on number of errors reported") - SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble") - Importpath = flag.String("p", "", "set expected package import to path") - Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") + Debug = flag.Bool("debug", false, "dump instructions as they are parsed") + OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument") + TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths") + Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library") + Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries") + AllErrors = flag.Bool("e", false, "no limit on number of errors reported") + SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble") + Importpath = flag.String("p", "", "set expected package import to path") + Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") + CompilingRuntime = flag.Bool("compilingRuntime", false, "source to be compiled is part of the Go runtime") ) var ( diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index 01c963ac72..149925d23f 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -52,7 +52,6 @@ func main() { case "all", "ret": ctxt.Retpoline = true } - compilingRuntime := objabi.IsRuntimePackagePath(*flags.Importpath) ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() @@ -75,7 +74,8 @@ func main() { var failedFile string for _, f := range flag.Args() { lexer := lex.NewLexer(f) - parser := asm.NewParser(ctxt, architecture, lexer, compilingRuntime) + parser := asm.NewParser(ctxt, architecture, lexer, + *flags.CompilingRuntime) ctxt.DiagFunc = func(format string, args ...interface{}) { diag = true log.Printf(format, args...) diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 398ed6bce1..11da38ebdf 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -832,6 +832,21 @@ func runInstall(pkg string, ch chan struct{}) { asmArgs = append(asmArgs, "-D", "GOMIPS64_"+gomips64) } goasmh := pathf("%s/go_asm.h", workdir) + if IsRuntimePackagePath(pkg) { + asmArgs = append(asmArgs, "-compilingRuntime") + if os.Getenv("GOEXPERIMENT") == "regabi" { + // In order to make it easier to port runtime assembly + // to the register ABI, we introduce a macro + // indicating the experiment is enabled. + // + // Note: a similar change also appears in + // cmd/go/internal/work/gc.go. + // + // TODO(austin): Remove this once we commit to the + // register ABI (#40724). + asmArgs = append(asmArgs, "-D=GOEXPERIMENT_REGABI=1") + } + } // Collect symabis from assembly code. var symabis string @@ -1733,3 +1748,23 @@ func cmdlist() { fatalf("write failed: %v", err) } } + +// IsRuntimePackagePath examines 'pkgpath' and returns TRUE if it +// belongs to the collection of "runtime-related" packages, including +// "runtime" itself, "reflect", "syscall", and the +// "runtime/internal/*" packages. See also the function of the same +// name in cmd/internal/objabi/path.go. +func IsRuntimePackagePath(pkgpath string) bool { + rval := false + switch pkgpath { + case "runtime": + rval = true + case "reflect": + rval = true + case "syscall": + rval = true + default: + rval = strings.HasPrefix(pkgpath, "runtime/internal") + } + return rval +} diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 56ad1872be..2df4a52ba5 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -292,14 +292,20 @@ func asmArgs(a *Action, p *load.Package) []interface{} { } } } - if p.ImportPath == "runtime" && objabi.Regabi_enabled != 0 { - // In order to make it easier to port runtime assembly - // to the register ABI, we introduce a macro - // indicating the experiment is enabled. - // - // TODO(austin): Remove this once we commit to the - // register ABI (#40724). - args = append(args, "-D=GOEXPERIMENT_REGABI=1") + if objabi.IsRuntimePackagePath(pkgpath) { + args = append(args, "-compilingRuntime") + if objabi.Regabi_enabled != 0 { + // In order to make it easier to port runtime assembly + // to the register ABI, we introduce a macro + // indicating the experiment is enabled. + // + // Note: a similar change also appears in + // cmd/dist/build.go. + // + // TODO(austin): Remove this once we commit to the + // register ABI (#40724). + args = append(args, "-D=GOEXPERIMENT_REGABI=1") + } } if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" { -- cgit v1.3 From 627959eb04ee0edc4a985a7526ed7fe838ad2573 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Wed, 14 Oct 2020 21:15:37 -0400 Subject: cmd/link: support cgo internal/linking on darwin/arm64 Cgo programs work as well. Still not enabled by default for now. Enable internal linking tests. Updates #38485. Change-Id: I8324a5c263fba221eb4e67d71207ca84fa241e6c Reviewed-on: https://go-review.googlesource.com/c/go/+/263637 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Than McIntosh --- misc/cgo/test/issue4029.c | 2 +- misc/cgo/test/issue4029.go | 3 +- misc/cgo/test/issue4029w.go | 2 +- src/cmd/dist/test.go | 7 +-- src/cmd/link/internal/arm64/asm.go | 91 ++++++++++++++++++++++++++++-- src/cmd/link/internal/ld/config.go | 3 - src/cmd/link/internal/loader/loader.go | 8 ++- src/cmd/link/internal/loadmacho/ldmacho.go | 57 +++++++++++-------- 8 files changed, 131 insertions(+), 42 deletions(-) (limited to 'src/cmd/dist') diff --git a/misc/cgo/test/issue4029.c b/misc/cgo/test/issue4029.c index e6a777fe64..e79c5a709c 100644 --- a/misc/cgo/test/issue4029.c +++ b/misc/cgo/test/issue4029.c @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build !windows,!static -// +build !darwin !internal_pie +// +build !darwin !internal_pie,!arm64 #include #include diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go index 8602ce19e2..b2d131833a 100644 --- a/misc/cgo/test/issue4029.go +++ b/misc/cgo/test/issue4029.go @@ -3,10 +3,11 @@ // license that can be found in the LICENSE file. // +build !windows,!static -// +build !darwin !internal_pie +// +build !darwin !internal_pie,!arm64 // Excluded in darwin internal linking PIE mode, as dynamic export is not // supported. +// Excluded in internal linking mode on darwin/arm64, as it is always PIE. package cgotest diff --git a/misc/cgo/test/issue4029w.go b/misc/cgo/test/issue4029w.go index de0cf2138a..b969bdd0fe 100644 --- a/misc/cgo/test/issue4029w.go +++ b/misc/cgo/test/issue4029w.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build windows static darwin,internal_pie +// +build windows static darwin,internal_pie darwin,arm64 package cgotest diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index bcb12f29fb..09d69f72ed 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -946,9 +946,6 @@ func (t *tester) internalLink() bool { if goos == "ios" { return false } - if goos == "darwin" && goarch == "arm64" { - return false - } // Internally linking cgo is incomplete on some architectures. // https://golang.org/issue/10373 // https://golang.org/issue/14449 @@ -964,7 +961,7 @@ func (t *tester) internalLink() bool { func (t *tester) internalLinkPIE() bool { switch goos + "-" + goarch { - case "darwin-amd64", + case "darwin-amd64", "darwin-arm64", "linux-amd64", "linux-arm64", "android-arm64", "windows-amd64", "windows-386", "windows-arm": @@ -1088,7 +1085,7 @@ func (t *tester) cgoTest(dt *distTest) error { pair := gohostos + "-" + goarch switch pair { - case "darwin-amd64", + case "darwin-amd64", "darwin-arm64", "openbsd-386", "openbsd-amd64", "windows-386", "windows-amd64": // test linkmode=external, but __thread not supported, so skip testtls. diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 585c96852f..e456411155 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -71,13 +71,13 @@ func gentext(ctxt *ld.Link, ldr *loader.Loader) { } func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { - targ := r.Sym() var targType sym.SymKind if targ != 0 { targType = ldr.SymType(targ) } + const pcrel = 1 switch r.Type() { default: if r.Type() >= objabi.ElfRelocOffset { @@ -201,6 +201,75 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_ARM64_LDST128) return true + + // Handle relocations found in Mach-O object files. + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_UNSIGNED*2: + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel: + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM64) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGE21*2 + pcrel, + objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGEOFF12*2: + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_PCREL) + return true + + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21*2 + pcrel, + objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12*2: + if targType != sym.SDYNIMPORT { + // have symbol + // turn MOVD sym@GOT (adrp+ldr) into MOVD $sym (adrp+add) + data := ldr.Data(s) + off := r.Off() + if int(off+3) >= len(data) { + ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + o := target.Arch.ByteOrder.Uint32(data[off:]) + su := ldr.MakeSymbolUpdater(s) + switch { + case (o>>24)&0x9f == 0x90: // adrp + // keep instruction unchanged, change relocation type below + case o>>24 == 0xf9: // ldr + // rewrite to add + o = (0x91 << 24) | (o & (1<<22 - 1)) + su.MakeWritable() + su.SetUint32(target.Arch, int64(off), o) + default: + ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + su.SetRelocType(rIdx, objabi.R_ARM64_PCREL) + return true + } + ld.AddGotSym(target, ldr, syms, targ, 0) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_GOT) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ))) + return true } // Reread the reloc to incorporate any changes in type above. @@ -671,14 +740,28 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade } o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) return val | int64(o0), noExtReloc, isOk - } else if (val>>24)&0x91 == 0x91 { - // R_AARCH64_ADD_ABS_LO12_NC + } else if (val>>24)&0x9f == 0x91 { + // ELF R_AARCH64_ADD_ABS_LO12_NC or Mach-O ARM64_RELOC_PAGEOFF12 // patch instruction: add t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) o1 := uint32(t&0xfff) << 10 return val | int64(o1), noExtReloc, isOk + } else if (val>>24)&0x3b == 0x39 { + // Mach-O ARM64_RELOC_PAGEOFF12 + // patch ldr/str(b/h/w/d/q) (integer or vector) instructions, which have different scaling factors. + // Mach-O uses same relocation type for them. + shift := uint32(val) >> 30 + if shift == 0 && (val>>20)&0x048 == 0x048 { // 128-bit vector load + shift = 4 + } + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&(1<> shift) << 10 + return val | int64(o1), noExtReloc, isOk } else { - ldr.Errorf(s, "unsupported instruction for %x R_PCRELARM64", val) + ldr.Errorf(s, "unsupported instruction for %x R_ARM64_PCREL", val) } case objabi.R_ARM64_LDST8: diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index 834c87d06b..a54b96dd5d 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -208,9 +208,6 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { if iscgo && objabi.GOOS == "android" { return true, objabi.GOOS + " does not support internal cgo" } - if iscgo && objabi.GOOS == "darwin" && objabi.GOARCH == "arm64" { - return true, objabi.GOOS + "/" + objabi.GOARCH + " does not support internal cgo" - } // When the race flag is set, the LLVM tsan relocatable file is linked // into the final binary, which means external linking is required because diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 47cac0441b..d861efcb13 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2622,11 +2622,15 @@ func (l *Loader) Dump() { fmt.Println("Nsyms:", len(l.objSyms)) fmt.Println("syms") for i := Sym(1); i < Sym(len(l.objSyms)); i++ { - pi := interface{}("") + pi := "" if l.IsExternal(i) { pi = fmt.Sprintf("", l.extIndex(i)) } - fmt.Println(i, l.SymName(i), l.SymType(i), pi) + sect := "" + if l.SymSect(i) != nil { + sect = l.SymSect(i).Name + } + fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect) } fmt.Println("symsByName") for name, i := range l.symsByName[0] { diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go index 864d80835b..d12f2bc2ac 100644 --- a/src/cmd/link/internal/loadmacho/ldmacho.go +++ b/src/cmd/link/internal/loadmacho/ldmacho.go @@ -43,7 +43,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld +// TODO(crawshaw): de-duplicate these symbols with cmd/link/internal/ld const ( MACHO_X86_64_RELOC_UNSIGNED = 0 MACHO_X86_64_RELOC_SIGNED = 1 @@ -172,11 +172,12 @@ const ( LdMachoCpuVax = 1 LdMachoCpu68000 = 6 LdMachoCpu386 = 7 - LdMachoCpuAmd64 = 0x1000007 + LdMachoCpuAmd64 = 1<<24 | 7 LdMachoCpuMips = 8 LdMachoCpu98000 = 10 LdMachoCpuHppa = 11 LdMachoCpuArm = 12 + LdMachoCpuArm64 = 1<<24 | 12 LdMachoCpu88000 = 13 LdMachoCpuSparc = 14 LdMachoCpu860 = 15 @@ -471,11 +472,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, switch arch.Family { default: return errorf("mach-o %s unimplemented", arch.Name) - case sys.AMD64: if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { return errorf("mach-o object but not amd64") } + case sys.ARM64: + if e != binary.LittleEndian || m.cputype != LdMachoCpuArm64 { + return errorf("mach-o object but not arm64") + } } m.cmd = make([]ldMachoCmd, ncmd) @@ -633,7 +637,9 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, } bld.SetType(l.SymType(outer)) - l.AddInteriorSym(outer, s) + if l.SymSize(outer) != 0 { // skip empty section (0-sized symbol) + l.AddInteriorSym(outer, s) + } bld.SetValue(int64(machsym.value - sect.addr)) if !l.AttrCgoExportDynamic(s) { @@ -722,27 +728,28 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, // Handle X86_64_RELOC_SIGNED referencing a section (rel.extrn == 0). p := l.Data(s) - if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED { - // Calculate the addend as the offset into the section. - // - // The rip-relative offset stored in the object file is encoded - // as follows: - // - // movsd 0x00000360(%rip),%xmm0 - // - // To get the absolute address of the value this rip-relative address is pointing - // to, we must add the address of the next instruction to it. This is done by - // taking the address of the relocation and adding 4 to it (since the rip-relative - // offset can at most be 32 bits long). To calculate the offset into the section the - // relocation is referencing, we subtract the vaddr of the start of the referenced - // section found in the original object file. - // - // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] - secaddr := c.seg.sect[rel.symnum-1].addr - - rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr) - } else { - rAdd = int64(int32(e.Uint32(p[rOff:]))) + if arch.Family == sys.AMD64 { + if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED { + // Calculate the addend as the offset into the section. + // + // The rip-relative offset stored in the object file is encoded + // as follows: + // + // movsd 0x00000360(%rip),%xmm0 + // + // To get the absolute address of the value this rip-relative address is pointing + // to, we must add the address of the next instruction to it. This is done by + // taking the address of the relocation and adding 4 to it (since the rip-relative + // offset can at most be 32 bits long). To calculate the offset into the section the + // relocation is referencing, we subtract the vaddr of the start of the referenced + // section found in the original object file. + // + // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] + secaddr := c.seg.sect[rel.symnum-1].addr + rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr) + } else { + rAdd = int64(int32(e.Uint32(p[rOff:]))) + } } // An unsigned internal relocation has a value offset -- cgit v1.3 From d595712540f00d980b1276ed25495ee7e05c1bfa Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Tue, 20 Oct 2020 09:06:42 -0400 Subject: cmd/asm: rename "compiling runtime" flag Rename the assembler "-compilingRuntime" flag to "-compiling-runtime", to be more consistent with the flag style of other Go commands. Change-Id: I8cc5cbf0b9b34d1dd4e9fa499d3fec8c1ef10b6e Reviewed-on: https://go-review.googlesource.com/c/go/+/263857 Trust: Than McIntosh Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Austin Clements Reviewed-by: Cherry Zhang --- src/cmd/asm/internal/flags/flags.go | 2 +- src/cmd/dist/build.go | 2 +- src/cmd/go/internal/work/gc.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index 0765439959..426e0156aa 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -24,7 +24,7 @@ var ( SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble") Importpath = flag.String("p", "", "set expected package import to path") Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") - CompilingRuntime = flag.Bool("compilingRuntime", false, "source to be compiled is part of the Go runtime") + CompilingRuntime = flag.Bool("compiling-runtime", false, "source to be compiled is part of the Go runtime") ) var ( diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 11da38ebdf..d902addb0c 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -833,7 +833,7 @@ func runInstall(pkg string, ch chan struct{}) { } goasmh := pathf("%s/go_asm.h", workdir) if IsRuntimePackagePath(pkg) { - asmArgs = append(asmArgs, "-compilingRuntime") + asmArgs = append(asmArgs, "-compiling-runtime") if os.Getenv("GOEXPERIMENT") == "regabi" { // In order to make it easier to port runtime assembly // to the register ABI, we introduce a macro diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 2df4a52ba5..e93031431c 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -293,7 +293,7 @@ func asmArgs(a *Action, p *load.Package) []interface{} { } } if objabi.IsRuntimePackagePath(pkgpath) { - args = append(args, "-compilingRuntime") + args = append(args, "-compiling-runtime") if objabi.Regabi_enabled != 0 { // In order to make it easier to port runtime assembly // to the register ABI, we introduce a macro -- cgit v1.3 From 431d58da69e8c36d654876e7808f971c5667649c Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 20 Oct 2020 11:01:46 +0200 Subject: all: add GOOS=ios GOARCH=amd64 target for the ios simulator The Go toolchain has supported the simulator for years, but always in buildmode=c-archive which is intrinsically externally linked and PIE. This CL moves that support from GOOS=darwin GOARCH=amd64 -tags=ios to just GOOS=ios GOARCH=amd64 to match the change for iOS devices. This change also forces external linking and defaults to buildmode=pie to support Go binaries in the default buildmode to run on the simulator. CL 255257 added the necessary support to the exec wrapper. Updates #38485 Fixes #42100 Change-Id: I6e6ee0e8d421be53b31e3d403880e5b9b880d031 Reviewed-on: https://go-review.googlesource.com/c/go/+/263798 Reviewed-by: Austin Clements Reviewed-by: Cherry Zhang Trust: Elias Naur --- src/cmd/dist/build.go | 1 + src/cmd/go/internal/load/pkg.go | 4 +++- src/cmd/go/internal/work/init.go | 5 ++++- src/cmd/internal/sys/supported.go | 1 + src/cmd/link/internal/ld/config.go | 8 +------- src/runtime/rt0_ios_amd64.s | 14 ++++++++++++++ 6 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 src/runtime/rt0_ios_amd64.s (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index d902addb0c..e46c33522d 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1557,6 +1557,7 @@ var cgoEnabled = map[string]bool{ "android/arm": true, "android/arm64": true, "ios/arm64": true, + "ios/amd64": true, "js/wasm": false, "netbsd/386": true, "netbsd/amd64": true, diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index f07bd3e075..29709a6dd3 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -1966,7 +1966,9 @@ func externalLinkingForced(p *Package) bool { if cfg.BuildContext.GOARCH != "arm64" { return true } - case "darwin", "ios": + case "ios": + return true + case "darwin": if cfg.BuildContext.GOARCH == "arm64" { return true } diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 81c4fb7465..d65c076c6a 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -168,7 +168,10 @@ func buildModeInit() { ldBuildmode = "pie" case "windows": ldBuildmode = "pie" - case "darwin", "ios": + case "ios": + codegenArg = "-shared" + ldBuildmode = "pie" + case "darwin": switch cfg.Goarch { case "arm64": codegenArg = "-shared" diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index c433a872be..ccc5b2245b 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -88,6 +88,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", + "ios/amd64", "ios/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 a54b96dd5d..54a94cebba 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -42,13 +42,7 @@ func (mode *BuildMode) Set(s string) error { *mode = BuildModeExe case "pie": switch objabi.GOOS { - case "aix", "android", "linux", "windows": - case "darwin": - switch objabi.GOARCH { - case "amd64", "arm64": - default: - return badmode() - } + case "aix", "android", "linux", "windows", "darwin", "ios": case "freebsd": switch objabi.GOARCH { case "amd64": diff --git a/src/runtime/rt0_ios_amd64.s b/src/runtime/rt0_ios_amd64.s new file mode 100644 index 0000000000..c6990324f4 --- /dev/null +++ b/src/runtime/rt0_ios_amd64.s @@ -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. + +#include "textflag.h" + +// internal linking executable entry point. +// ios/amd64 only supports external linking. +TEXT _rt0_amd64_ios(SB),NOSPLIT|NOFRAME,$0 + UNDEF + +// library entry point. +TEXT _rt0_amd64_ios_lib(SB),NOSPLIT|NOFRAME,$0 + JMP _rt0_amd64_darwin_lib(SB) -- cgit v1.3 From d75b7f9648bb4968a5d980c4a4b7aa6ca22c4de5 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 25 Aug 2020 17:52:46 +1000 Subject: cmd/dist: add openbsd/mips64 as incomplete port Update #40995 Change-Id: Id497f7688b00658b50feb7338157e0411b861910 Reviewed-on: https://go-review.googlesource.com/c/go/+/250578 Trust: Joel Sing Reviewed-by: Tobias Klauser --- src/cmd/dist/build.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index e46c33522d..d822a83e44 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1567,6 +1567,7 @@ var cgoEnabled = map[string]bool{ "openbsd/amd64": true, "openbsd/arm": true, "openbsd/arm64": true, + "openbsd/mips64": false, "plan9/386": false, "plan9/amd64": false, "plan9/arm": false, @@ -1579,7 +1580,8 @@ var cgoEnabled = map[string]bool{ // List of platforms which are supported but not complete yet. These get // filtered out of cgoEnabled for 'dist list'. See golang.org/issue/28944 var incomplete = map[string]bool{ - "linux/sparc64": true, + "linux/sparc64": true, + "openbsd/mips64": true, } func needCC() bool { -- cgit v1.3 From d03437a7c228c6842ad81b4d6e7dabea913cdc5f Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Sat, 24 Oct 2020 20:21:48 +0200 Subject: cmd/dist: document why test fails on incomplete ports It might not be obvious from reading the code why we consider the test as failed on incomplete ports even though it passed. Add a comment documenting this behavior, as suggested by Dmitri in CL 155839. Change-Id: I3eb7db27d01d63db277172381e5fa51577dad941 Reviewed-on: https://go-review.googlesource.com/c/go/+/264682 Trust: Tobias Klauser Reviewed-by: Dmitri Shuralyov --- src/cmd/dist/test.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 09d69f72ed..dbe55a7c38 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -217,6 +217,9 @@ func (t *tester) run() { fmt.Println("\nFAILED") xexit(1) } else if incomplete[goos+"/"+goarch] { + // The test succeeded, but consider it as failed so we don't + // forget to remove the port from the incomplete map once the + // port is complete. fmt.Println("\nFAILED (incomplete port)") xexit(1) } else if t.partial { -- cgit v1.3 From 4f2d2134764b0b7654105ac2d6f75a75cdc1de56 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 2 Jun 2020 23:34:06 +1000 Subject: cmd/compile,cmd/internal/sys: enable additional build modes on linux/riscv64 Enable c-archive, c-shared, shared and pie build modes for linux/riscv64. Change-Id: I15a8a51b84dbbb82a5b6592aec84a7f09f0cc37f Reviewed-on: https://go-review.googlesource.com/c/go/+/263457 Trust: Joel Sing Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/dist/test.go | 8 ++++---- src/cmd/go/go_test.go | 2 +- src/cmd/internal/sys/supported.go | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 4b401f2aa4..0b65e8a0b4 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -130,7 +130,7 @@ func hidePanic() { // supportsDynlink reports whether or not the code generator for the given // architecture supports the -shared and -dynlink flags. func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) } // timing data for compiler phases diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index dbe55a7c38..622d0cee83 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -983,7 +983,7 @@ func (t *tester) supportedBuildmode(mode string) bool { switch pair { case "aix-ppc64", "darwin-amd64", "darwin-arm64", "ios-arm64", - "linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x", + "linux-amd64", "linux-386", "linux-ppc64le", "linux-riscv64", "linux-s390x", "freebsd-amd64", "windows-amd64", "windows-386": return true @@ -991,7 +991,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "c-shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "darwin-amd64", "darwin-arm64", "freebsd-amd64", "android-arm", "android-arm64", "android-386", @@ -1001,7 +1001,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x": + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x": return true } return false @@ -1020,7 +1020,7 @@ func (t *tester) supportedBuildmode(mode string) bool { case "pie": switch pair { case "aix/ppc64", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "android-amd64", "android-arm", "android-arm64", "android-386": return true case "darwin-amd64", "darwin-arm64": diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index e9c26d161a..d1bd516a5d 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2070,7 +2070,7 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "windows/386", "windows/amd64", "windows/arm": diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index ccc5b2245b..1d813bbb47 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -67,7 +67,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x", + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -84,7 +84,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -97,7 +97,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "shared": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x": return true } return false -- cgit v1.3 From b3f7f60129b822978115717912f4d477a46e8467 Mon Sep 17 00:00:00 2001 From: xd Date: Wed, 14 Oct 2020 11:02:49 -0700 Subject: cmd/dist: fix build failure of misc/cgo/test on arm64 misc/cgo/test fails in 'dist test' on arm64 if the C compiler is of GCC-9.4 or above and its 'outline atomics' feature is enabled, since the internal linking hasn't yet supported "__attribute__((constructor))" and also mis-handles hidden visibility. This change addresses the problem by skipping the internal linking cases of misc/cgo/test on linux/arm64. It fixes 'dist test' failure only, user is expected to pass a GCC option '-mno-outline-atomics' via CGO_CFLAGS if running into the same problem when building cgo programs using internal linking. Updates #39466 Change-Id: I57f9e85fca881e5fd2dae6c1b4446bce9e0c1975 Reviewed-on: https://go-review.googlesource.com/c/go/+/262357 Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Trust: Emmanuel Odeke --- src/cmd/dist/test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 622d0cee83..3cf49dc8ad 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1081,7 +1081,12 @@ func (t *tester) cgoTest(dt *distTest) error { cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=auto") - if t.internalLink() { + // Skip internal linking cases on arm64 to support GCC-9.4 and above, + // only for linux, conservatively. + // See issue #39466. + skipInternalLink := goarch == "arm64" && goos == "linux" + + if t.internalLink() && !skipInternalLink { cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal") cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=internal") } @@ -1157,7 +1162,7 @@ func (t *tester) cgoTest(dt *distTest) error { if t.supportedBuildmode("pie") { t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie") - if t.internalLink() && t.internalLinkPIE() { + if t.internalLink() && t.internalLinkPIE() && !skipInternalLink { t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie") } t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-buildmode=pie") -- cgit v1.3 From 40d1ec588f492f63637b59e933d6c682d66659a4 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 24 Oct 2020 22:02:50 -0400 Subject: cmd/dist: use GOOS=ios for iOS simulator test Updates #42100. Change-Id: Ib59432bea99a9dd6e25fbd759b31e423dc250f42 Reviewed-on: https://go-review.googlesource.com/c/go/+/265197 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/cmd/dist/test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 3cf49dc8ad..aeffc2659f 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -466,13 +466,14 @@ func (t *tester) registerTests() { }) } - // Test the ios build tag on darwin/amd64 for the iOS simulator. - if goos == "darwin" && goarch == "amd64" { + // Test ios/amd64 for the iOS simulator. + if goos == "darwin" && goarch == "amd64" && t.cgoEnabled { t.tests = append(t.tests, distTest{ name: "amd64ios", - heading: "ios tag on darwin/amd64", + heading: "GOOS=ios on darwin/amd64", fn: func(dt *distTest) error { - t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=ios", "-run=SystemRoots", "crypto/x509") + cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-run=SystemRoots", "crypto/x509") + cmd.Env = append(os.Environ(), "GOOS=ios", "CGO_ENABLED=1") return nil }, }) -- cgit v1.3 From 2e6f50020cacd68f24e413d24d4c3e92e0c9e69f Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Fri, 30 Oct 2020 04:32:18 +1100 Subject: Revert "cmd/compile,cmd/internal/sys: enable additional build modes on linux/riscv64" This reverts CL 263457. It turns out that this still missed changes to cmd/link/internal/ld/config.go and some of these build modes also fail once cgo is enabled. Disable again for now. Change-Id: Iaf40d44e1551afd5b040d357f04af134f55a64a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/266317 Reviewed-by: Cherry Zhang Trust: Joel Sing --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/dist/test.go | 8 ++++---- src/cmd/go/go_test.go | 2 +- src/cmd/internal/sys/supported.go | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 0b65e8a0b4..4b401f2aa4 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -130,7 +130,7 @@ func hidePanic() { // supportsDynlink reports whether or not the code generator for the given // architecture supports the -shared and -dynlink flags. func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) } // timing data for compiler phases diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index aeffc2659f..7c454dd38d 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -984,7 +984,7 @@ func (t *tester) supportedBuildmode(mode string) bool { switch pair { case "aix-ppc64", "darwin-amd64", "darwin-arm64", "ios-arm64", - "linux-amd64", "linux-386", "linux-ppc64le", "linux-riscv64", "linux-s390x", + "linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x", "freebsd-amd64", "windows-amd64", "windows-386": return true @@ -992,7 +992,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "c-shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", "darwin-amd64", "darwin-arm64", "freebsd-amd64", "android-arm", "android-arm64", "android-386", @@ -1002,7 +1002,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x": + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x": return true } return false @@ -1021,7 +1021,7 @@ func (t *tester) supportedBuildmode(mode string) bool { case "pie": switch pair { case "aix/ppc64", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", + "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", "darwin-arm64": diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index c7ca73b5b5..1fb1325519 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2021,7 +2021,7 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "windows/386", "windows/amd64", "windows/arm": diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index afc81381fd..3c750774ed 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -67,7 +67,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", + 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/arm64", @@ -84,7 +84,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", + 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/arm64", @@ -97,7 +97,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "shared": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x": + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": return true } return false -- cgit v1.3 From b53df56001a6bd375b1dc2026908f331729cd731 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Thu, 29 Oct 2020 11:27:17 -0400 Subject: runtime, cmd: support race detector on darwin/arm64 https://reviews.llvm.org/D90435 is the counterpart in LLVM TSAN. race_linux_arm64.syso is built with LLVM commit 00da38ce2d36c07f12c287dc515d37bb7bc410e9 on a macOS/ARM64 machine. (It is not built on a builder with golang.org/x/build/cmd/racebuild as we don't have darwin/arm64 builder for now.) Updates #38485. Change-Id: I391efdacd9480197e308370bfccd05777deb4aee Reviewed-on: https://go-review.googlesource.com/c/go/+/266373 Trust: Cherry Zhang Reviewed-by: Ian Lance Taylor --- src/cmd/dist/test.go | 4 +++- src/cmd/go/internal/work/init.go | 2 +- src/cmd/internal/sys/supported.go | 4 +++- src/cmd/link/internal/ld/config.go | 2 +- src/cmd/link/internal/loadmacho/ldmacho.go | 12 ++++++++++-- src/runtime/race/README | 1 + src/runtime/race/race.go | 2 +- src/runtime/race/race_darwin_arm64.syso | Bin 0 -> 438936 bytes src/runtime/race_arm64.s | 15 +++++++++++++++ 9 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/runtime/race/race_darwin_arm64.syso (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 7c454dd38d..9c25392cc0 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1619,7 +1619,9 @@ func raceDetectorSupported(goos, goarch string) bool { switch goos { case "linux": return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" - case "darwin", "freebsd", "netbsd", "windows": + case "darwin": + return goarch == "amd64" || goarch == "arm64" + case "freebsd", "netbsd", "windows": return goarch == "amd64" default: return false diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index d65c076c6a..102def4838 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -79,7 +79,7 @@ func instrumentInit() { } if cfg.BuildRace { if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) { - fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n", flag.Args()[0]) base.SetExitStatus(2) base.Exit() } diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index 3c750774ed..69d7591440 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -13,7 +13,9 @@ func RaceDetectorSupported(goos, goarch string) bool { switch goos { case "linux": return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" - case "darwin", "freebsd", "netbsd", "windows": + case "darwin": + return goarch == "amd64" || goarch == "arm64" + case "freebsd", "netbsd", "windows": return goarch == "amd64" default: return false diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index 0cb3cc25c0..cd64d86a4a 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -206,7 +206,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { // When the race flag is set, the LLVM tsan relocatable file is linked // into the final binary, which means external linking is required because // internal linking does not support it. - if *flagRace && ctxt.Arch.InFamily(sys.PPC64) { + if *flagRace && (ctxt.Arch.InFamily(sys.PPC64) || ctxt.IsDarwin() && ctxt.IsARM64()) { return true, "race on " + objabi.GOARCH } diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go index d12f2bc2ac..d26869e23a 100644 --- a/src/cmd/link/internal/loadmacho/ldmacho.go +++ b/src/cmd/link/internal/loadmacho/ldmacho.go @@ -47,7 +47,7 @@ THE SOFTWARE. const ( MACHO_X86_64_RELOC_UNSIGNED = 0 MACHO_X86_64_RELOC_SIGNED = 1 - MACHO_FAKE_GOTPCREL = 100 + MACHO_ARM64_RELOC_ADDEND = 10 ) type ldMachoObj struct { @@ -707,11 +707,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, } sb := l.MakeSymbolUpdater(sect.sym) + var rAdd int64 for j := uint32(0); j < sect.nreloc; j++ { var ( rOff int32 rSize uint8 - rAdd int64 rType objabi.RelocType rSym loader.Sym ) @@ -722,6 +722,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, return errorf("%v: unexpected scattered relocation", s) } + if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND { + // Two relocations. This addend will be applied to the next one. + rAdd = int64(rel.symnum) + continue + } else { + rAdd = 0 + } + rSize = rel.length rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel) rOff = int32(rel.addr) diff --git a/src/runtime/race/README b/src/runtime/race/README index b36d82ccfd..178ab94ab5 100644 --- a/src/runtime/race/README +++ b/src/runtime/race/README @@ -11,3 +11,4 @@ race_linux_ppc64le.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 race_netbsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. race_windows_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. race_linux_arm64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. +race_darwin_arm64.syso built with LLVM 00da38ce2d36c07f12c287dc515d37bb7bc410e9 and Go fe70a3a0fd31441bcbb9932ecab11a6083cf2119. diff --git a/src/runtime/race/race.go b/src/runtime/race/race.go index c894de5f72..d6a14b79e7 100644 --- a/src/runtime/race/race.go +++ b/src/runtime/race/race.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build race,linux,amd64 race,freebsd,amd64 race,netbsd,amd64 race,darwin,amd64 race,windows,amd64 race,linux,ppc64le race,linux,arm64 +// +build race,linux,amd64 race,freebsd,amd64 race,netbsd,amd64 race,darwin,amd64 race,windows,amd64 race,linux,ppc64le race,linux,arm64 race,darwin,arm64 package race diff --git a/src/runtime/race/race_darwin_arm64.syso b/src/runtime/race/race_darwin_arm64.syso new file mode 100644 index 0000000000..f6eaa62ae3 Binary files /dev/null and b/src/runtime/race/race_darwin_arm64.syso differ diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s index 9b909ac021..59373a9f3f 100644 --- a/src/runtime/race_arm64.s +++ b/src/runtime/race_arm64.s @@ -25,8 +25,15 @@ // The race ctx, ThreadState *thr below, is passed in R0 and loaded in racecalladdr. +#ifdef TLS_darwin +#define TP_ALIGN AND $~7, R0 +#else +#define TP_ALIGN +#endif + #define load_g \ MRS_TPIDR_R0 \ + TP_ALIGN \ MOVD runtime·tls_g(SB), R11 \ ADD R11, R0 \ MOVD 0(R0), g @@ -423,7 +430,13 @@ TEXT runtime·racecallbackthunk(SB), NOSPLIT|NOFRAME, $0 // benefit from this fast path. CBNZ R0, rest MOVD g, R13 +#ifdef TLS_darwin + MOVD R27, R12 // save R27 a.k.a. REGTMP (callee-save in C). load_g clobbers it +#endif load_g +#ifdef TLS_darwin + MOVD R12, R27 +#endif MOVD g_m(g), R0 MOVD m_p(R0), R0 MOVD p_raceprocctx(R0), R0 @@ -477,5 +490,7 @@ noswitch: BL runtime·racecallback(SB) JMP ret +#ifndef TLSG_IS_VARIABLE // tls_g, g value for each thread in TLS GLOBL runtime·tls_g+0(SB), TLSBSS+DUPOK, $8 +#endif -- cgit v1.3 From 393f2bb067088cdbdb8d5848e6880b2ce65ddaf9 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 19 May 2020 18:56:01 +1000 Subject: cmd/dist,cmd/go,runtime: add support for cgo on linux/riscv64 Fixes #36641 Change-Id: I51868d83ce341d78d33b221d184c5a5110c60d14 Reviewed-on: https://go-review.googlesource.com/c/go/+/263598 Trust: Joel Sing Run-TryBot: Joel Sing TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- misc/cgo/test/testdata/issue9400/asm_riscv64.s | 31 ++++ src/cmd/dist/build.go | 2 +- src/cmd/dist/test.go | 6 +- .../go/testdata/script/build_plugin_non_main.txt | 3 +- src/cmd/nm/nm_cgo_test.go | 2 +- src/runtime/asm_riscv64.s | 190 +++++++++++++++++++-- src/runtime/cgo/asm_riscv64.s | 84 +++++++++ src/runtime/cgo/gcc_linux_riscv64.c | 74 ++++++++ src/runtime/cgo/gcc_riscv64.S | 80 +++++++++ src/runtime/cgocall.go | 2 +- src/runtime/tls_riscv64.s | 16 +- 11 files changed, 469 insertions(+), 21 deletions(-) create mode 100644 misc/cgo/test/testdata/issue9400/asm_riscv64.s create mode 100644 src/runtime/cgo/asm_riscv64.s create mode 100644 src/runtime/cgo/gcc_linux_riscv64.c create mode 100644 src/runtime/cgo/gcc_riscv64.S (limited to 'src/cmd/dist') diff --git a/misc/cgo/test/testdata/issue9400/asm_riscv64.s b/misc/cgo/test/testdata/issue9400/asm_riscv64.s new file mode 100644 index 0000000000..20fcc0066d --- /dev/null +++ b/misc/cgo/test/testdata/issue9400/asm_riscv64.s @@ -0,0 +1,31 @@ +// 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. + +// +build riscv64 +// +build !gccgo + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADD $(1024*8), X2 + + // Ask signaller to setgid + MOV $1, X5 + FENCE + MOVW X5, ·Baton(SB) + FENCE + + // Wait for setgid completion +loop: + FENCE + MOVW ·Baton(SB), X5 + OR X6, X6, X6 // hint that we're in a spin loop + BNE ZERO, X5, loop + FENCE + + // Restore stack + ADD $(-1024*8), X2 + RET diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index d822a83e44..20cb04d797 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1549,7 +1549,7 @@ var cgoEnabled = map[string]bool{ "linux/mipsle": true, "linux/mips64": true, "linux/mips64le": true, - "linux/riscv64": false, // Issue 36641 + "linux/riscv64": true, "linux/s390x": true, "linux/sparc64": true, "android/386": true, diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 9c25392cc0..d12a52b1cc 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -953,7 +953,7 @@ func (t *tester) internalLink() bool { // Internally linking cgo is incomplete on some architectures. // https://golang.org/issue/10373 // https://golang.org/issue/14449 - if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" { + if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" { return false } if goos == "aix" { @@ -1117,8 +1117,8 @@ func (t *tester) cgoTest(dt *distTest) error { "android-arm", "android-arm64", "dragonfly-amd64", "freebsd-386", "freebsd-amd64", "freebsd-arm", - "linux-386", "linux-amd64", "linux-arm", "linux-ppc64le", "linux-s390x", - "netbsd-386", "netbsd-amd64", "linux-arm64": + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", + "netbsd-386", "netbsd-amd64": cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") diff --git a/src/cmd/go/testdata/script/build_plugin_non_main.txt b/src/cmd/go/testdata/script/build_plugin_non_main.txt index dba80c29ad..3c82dced73 100644 --- a/src/cmd/go/testdata/script/build_plugin_non_main.txt +++ b/src/cmd/go/testdata/script/build_plugin_non_main.txt @@ -1,5 +1,6 @@ -# Plugins are only supported on linux,cgo and darwin,cgo. +# Plugins are only supported on linux,cgo (!riscv64) and darwin,cgo. [!linux] [!darwin] skip +[linux] [riscv64] skip [!cgo] skip go build -n testdep diff --git a/src/cmd/nm/nm_cgo_test.go b/src/cmd/nm/nm_cgo_test.go index 9a257e0ed2..e0414e6b22 100644 --- a/src/cmd/nm/nm_cgo_test.go +++ b/src/cmd/nm/nm_cgo_test.go @@ -24,7 +24,7 @@ func canInternalLink() bool { } case "linux": switch runtime.GOARCH { - case "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le": + case "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le", "riscv64": return false } case "openbsd": diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s index caaf42a7d0..01b42dc3de 100644 --- a/src/runtime/asm_riscv64.s +++ b/src/runtime/asm_riscv64.s @@ -9,10 +9,9 @@ // func rt0_go() TEXT runtime·rt0_go(SB),NOSPLIT,$0 // X2 = stack; A0 = argc; A1 = argv - ADD $-24, X2 - MOV A0, 8(X2) // argc - MOV A1, 16(X2) // argv + MOV A0, 8(X2) // argc + MOV A1, 16(X2) // argv // create istack out of the given (operating system) stack. // _cgo_init may update stackguard. @@ -28,10 +27,10 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0 MOV _cgo_init(SB), T0 BEQ T0, ZERO, nocgo - MOV ZERO, A3 // arg 3: not used - MOV ZERO, A2 // arg 2: not used + MOV ZERO, A3 // arg 3: not used + MOV ZERO, A2 // arg 2: not used MOV $setg_gcc<>(SB), A1 // arg 1: setg - MOV g, A0 // arg 0: G + MOV g, A0 // arg 0: G JALR RA, T0 nocgo: @@ -313,10 +312,62 @@ TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8 CALL runtime·badctxt(SB) RET +// Save state of caller into g->sched. Smashes X31. +TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0 + MOV X1, (g_sched+gobuf_pc)(g) + MOV X2, (g_sched+gobuf_sp)(g) + MOV ZERO, (g_sched+gobuf_lr)(g) + MOV ZERO, (g_sched+gobuf_ret)(g) + // Assert ctxt is zero. See func save. + MOV (g_sched+gobuf_ctxt)(g), X31 + BEQ ZERO, X31, 2(PC) + CALL runtime·badctxt(SB) + RET + // func asmcgocall(fn, arg unsafe.Pointer) int32 +// Call fn(arg) on the scheduler stack, +// aligned appropriately for the gcc ABI. +// See cgocall.go for more details. TEXT ·asmcgocall(SB),NOSPLIT,$0-20 - // TODO(jsing): Add support for cgo - issue #36641. - WORD $0 // crash + MOV fn+0(FP), X5 + MOV arg+8(FP), X10 + + MOV X2, X8 // save original stack pointer + MOV g, X9 + + // Figure out if we need to switch to m->g0 stack. + // We get called to create new OS threads too, and those + // come in on the m->g0 stack already. + MOV g_m(g), X6 + MOV m_g0(X6), X7 + BEQ X7, g, g0 + + CALL gosave<>(SB) + MOV X7, g + CALL runtime·save_g(SB) + MOV (g_sched+gobuf_sp)(g), X2 + + // Now on a scheduling stack (a pthread-created stack). +g0: + // Save room for two of our pointers. + ADD $-16, X2 + MOV X9, 0(X2) // save old g on stack + MOV (g_stack+stack_hi)(X9), X9 + SUB X8, X9, X8 + MOV X8, 8(X2) // save depth in old g stack (can't just save SP, as stack might be copied during a callback) + + JALR RA, (X5) + + // Restore g, stack pointer. X10 is return value. + MOV 0(X2), g + CALL runtime·save_g(SB) + MOV (g_stack+stack_hi)(g), X5 + MOV 8(X2), X6 + SUB X6, X5, X6 + MOV X6, X2 + + MOVW X10, ret+16(FP) + RET // func asminit() TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 @@ -444,6 +495,21 @@ CALLFN(·call268435456, 268435456) CALLFN(·call536870912, 536870912) CALLFN(·call1073741824, 1073741824) +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT _cgo_topofstack(SB),NOSPLIT,$8 + // g (X27) and REG_TMP (X31) might be clobbered by load_g. + // X27 is callee-save in the gcc calling convention, so save it. + MOV g, savedX27-8(SP) + + CALL runtime·load_g(SB) + MOV g_m(g), X5 + MOV m_curg(X5), X5 + MOV (g_stack+stack_hi)(X5), X10 // return value in X10 + + MOV savedX27-8(SP), g + RET + // func goexit(neverCallThisFunction) // The top-most function running on a goroutine // returns to goexit+PCQuantum. @@ -453,11 +519,111 @@ TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0 // traceback from goexit1 must hit code range of goexit MOV ZERO, ZERO // NOP -// cgocallback(fn, frame unsafe.Pointer, ctxt uintptr) +// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr) // See cgocall.go for more details. -TEXT ·cgocallback(SB),NOSPLIT,$0-24 - // TODO(jsing): Add support for cgo - issue #36641. - WORD $0 // crash +TEXT ·cgocallback(SB),NOSPLIT,$24-24 + NO_LOCAL_POINTERS + + // Load m and g from thread-local storage. + MOVBU runtime·iscgo(SB), X5 + BEQ ZERO, X5, nocgo + CALL runtime·load_g(SB) +nocgo: + + // If g is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call. + BEQ ZERO, g, needm + + MOV g_m(g), X5 + MOV X5, savedm-8(SP) + JMP havem + +needm: + MOV g, savedm-8(SP) // g is zero, so is m. + MOV $runtime·needm(SB), X6 + JALR RA, X6 + + // Set m->sched.sp = SP, so that if a panic happens + // during the function we are about to execute, it will + // have a valid SP to run on the g0 stack. + // The next few lines (after the havem label) + // will save this SP onto the stack and then write + // the same SP back to m->sched.sp. That seems redundant, + // but if an unrecovered panic happens, unwindm will + // restore the g->sched.sp from the stack location + // and then systemstack will try to use it. If we don't set it here, + // that restored SP will be uninitialized (typically 0) and + // will not be usable. + MOV g_m(g), X5 + MOV m_g0(X5), X6 + MOV X2, (g_sched+gobuf_sp)(X6) + +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. + // NOTE: unwindm knows that the saved g->sched.sp is at 8(X2) aka savedsp-24(SP). + MOV m_g0(X5), X6 + MOV (g_sched+gobuf_sp)(X6), X7 + MOV X7, savedsp-24(SP) // must match frame size + MOV X2, (g_sched+gobuf_sp)(X6) + + // Switch to m->curg stack and call runtime.cgocallbackg. + // Because we are taking over the execution of m->curg + // but *not* resuming what had been running, we need to + // save that information (m->curg->sched) so we can restore it. + // We can restore m->curg->sched.sp easily, because calling + // runtime.cgocallbackg leaves SP unchanged upon return. + // To save m->curg->sched.pc, we push it onto the curg stack and + // open a frame the same size as cgocallback's g0 frame. + // Once we switch to the curg stack, the pushed PC will appear + // to be the return PC of cgocallback, so that the traceback + // will seamlessly trace back into the earlier calls. + MOV m_curg(X5), g + CALL runtime·save_g(SB) + MOV (g_sched+gobuf_sp)(g), X6 // prepare stack as X6 + MOV (g_sched+gobuf_pc)(g), X7 + MOV X7, -(24+8)(X6) // "saved LR"; must match frame size + // Gather our arguments into registers. + MOV fn+0(FP), X7 + MOV frame+8(FP), X8 + MOV ctxt+16(FP), X9 + MOV $-(24+8)(X6), X2 // switch stack; must match frame size + MOV X7, 8(X2) + MOV X8, 16(X2) + MOV X9, 24(X2) + CALL runtime·cgocallbackg(SB) + + // Restore g->sched (== m->curg->sched) from saved values. + MOV 0(X2), X7 + MOV X7, (g_sched+gobuf_pc)(g) + MOV $(24+8)(X2), X6 // must match frame size + MOV X6, (g_sched+gobuf_sp)(g) + + // Switch back to m->g0's stack and restore m->g0->sched.sp. + // (Unlike m->curg, the g0 goroutine never uses sched.pc, + // so we do not have to restore it.) + MOV g_m(g), X5 + MOV m_g0(X5), g + CALL runtime·save_g(SB) + MOV (g_sched+gobuf_sp)(g), X2 + MOV savedsp-24(SP), X6 // must match frame size + MOV X6, (g_sched+gobuf_sp)(g) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + MOV savedm-8(SP), X5 + BNE ZERO, X5, droppedm + MOV $runtime·dropm(SB), X6 + JALR RA, X6 +droppedm: + + // Done! + RET TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 EBREAK diff --git a/src/runtime/cgo/asm_riscv64.s b/src/runtime/cgo/asm_riscv64.s new file mode 100644 index 0000000000..b4ddbb020f --- /dev/null +++ b/src/runtime/cgo/asm_riscv64.s @@ -0,0 +1,84 @@ +// 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. + +// +build riscv64 + +#include "textflag.h" + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * Push arguments for fn (X10, X11, X13), along with all callee-save + * registers. Note that at procedure entry the first argument is at + * 8(X2). + */ + ADD $(-8*31), X2 + MOV X10, (8*1)(X2) // fn unsafe.Pointer + MOV X11, (8*2)(X2) // a unsafe.Pointer + MOV X13, (8*3)(X2) // ctxt uintptr + MOV X8, (8*4)(X2) + MOV X9, (8*5)(X2) + MOV X18, (8*6)(X2) + MOV X19, (8*7)(X2) + MOV X20, (8*8)(X2) + MOV X21, (8*9)(X2) + MOV X22, (8*10)(X2) + MOV X23, (8*11)(X2) + MOV X24, (8*12)(X2) + MOV X25, (8*13)(X2) + MOV X26, (8*14)(X2) + MOV g, (8*15)(X2) + MOV X3, (8*16)(X2) + MOV X4, (8*17)(X2) + MOV X1, (8*18)(X2) + MOVD F8, (8*19)(X2) + MOVD F9, (8*20)(X2) + MOVD F18, (8*21)(X2) + MOVD F19, (8*22)(X2) + MOVD F20, (8*23)(X2) + MOVD F21, (8*24)(X2) + MOVD F22, (8*25)(X2) + MOVD F23, (8*26)(X2) + MOVD F24, (8*27)(X2) + MOVD F25, (8*28)(X2) + MOVD F26, (8*29)(X2) + MOVD F27, (8*30)(X2) + + // Initialize Go ABI environment + CALL runtime·load_g(SB) + CALL runtime·cgocallback(SB) + + MOV (8*4)(X2), X8 + MOV (8*5)(X2), X9 + MOV (8*6)(X2), X18 + MOV (8*7)(X2), X19 + MOV (8*8)(X2), X20 + MOV (8*9)(X2), X21 + MOV (8*10)(X2), X22 + MOV (8*11)(X2), X23 + MOV (8*12)(X2), X24 + MOV (8*13)(X2), X25 + MOV (8*14)(X2), X26 + MOV (8*15)(X2), g + MOV (8*16)(X2), X3 + MOV (8*17)(X2), X4 + MOV (8*18)(X2), X1 + MOVD (8*19)(X2), F8 + MOVD (8*20)(X2), F9 + MOVD (8*21)(X2), F18 + MOVD (8*22)(X2), F19 + MOVD (8*23)(X2), F20 + MOVD (8*24)(X2), F21 + MOVD (8*25)(X2), F22 + MOVD (8*26)(X2), F23 + MOVD (8*27)(X2), F24 + MOVD (8*28)(X2), F25 + MOVD (8*29)(X2), F26 + MOVD (8*30)(X2), F27 + ADD $(8*31), X2 + + RET diff --git a/src/runtime/cgo/gcc_linux_riscv64.c b/src/runtime/cgo/gcc_linux_riscv64.c new file mode 100644 index 0000000000..22b76c2fcc --- /dev/null +++ b/src/runtime/cgo/gcc_linux_riscv64.c @@ -0,0 +1,74 @@ +// 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. + +#include +#include +#include +#include "libcgo.h" +#include "libcgo_unix.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase); +static void (*setg_gcc)(void*); + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + // Not sure why the memset is necessary here, + // but without it, we get a bogus stack size + // out of pthread_attr_getstacksize. C'est la Linux. + memset(&attr, 0, sizeof attr); + pthread_attr_init(&attr); + size = 0; + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + pthread_attr_t attr; + size_t size; + + setg_gcc = setg; + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stacklo = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); + + if (x_cgo_inittls) { + x_cgo_inittls(tlsg, tlsbase); + } +} diff --git a/src/runtime/cgo/gcc_riscv64.S b/src/runtime/cgo/gcc_riscv64.S new file mode 100644 index 0000000000..f429dc64ee --- /dev/null +++ b/src/runtime/cgo/gcc_riscv64.S @@ -0,0 +1,80 @@ +// 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. + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard RISCV ELF psABI, where x8-x9, x18-x27, f8-f9 and + * f18-f27 are callee-save, so they must be saved explicitly, along with + * x1 (LR). + */ +.globl crosscall1 +crosscall1: + sd x1, -200(sp) + addi sp, sp, -200 + sd x8, 8(sp) + sd x9, 16(sp) + sd x18, 24(sp) + sd x19, 32(sp) + sd x20, 40(sp) + sd x21, 48(sp) + sd x22, 56(sp) + sd x23, 64(sp) + sd x24, 72(sp) + sd x25, 80(sp) + sd x26, 88(sp) + sd x27, 96(sp) + fsd f8, 104(sp) + fsd f9, 112(sp) + fsd f18, 120(sp) + fsd f19, 128(sp) + fsd f20, 136(sp) + fsd f21, 144(sp) + fsd f22, 152(sp) + fsd f23, 160(sp) + fsd f24, 168(sp) + fsd f25, 176(sp) + fsd f26, 184(sp) + fsd f27, 192(sp) + + // a0 = *fn, a1 = *setg_gcc, a2 = *g + mv s1, a0 + mv s0, a1 + mv a0, a2 + jalr ra, s0 // call setg_gcc (clobbers x30 aka g) + jalr ra, s1 // call fn + + ld x1, 0(sp) + ld x8, 8(sp) + ld x9, 16(sp) + ld x18, 24(sp) + ld x19, 32(sp) + ld x20, 40(sp) + ld x21, 48(sp) + ld x22, 56(sp) + ld x23, 64(sp) + ld x24, 72(sp) + ld x25, 80(sp) + ld x26, 88(sp) + ld x27, 96(sp) + fld f8, 104(sp) + fld f9, 112(sp) + fld f18, 120(sp) + fld f19, 128(sp) + fld f20, 136(sp) + fld f21, 144(sp) + fld f22, 152(sp) + fld f23, 160(sp) + fld f24, 168(sp) + fld f25, 176(sp) + fld f26, 184(sp) + fld f27, 192(sp) + addi sp, sp, 200 + + jr ra + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 9bca279318..20cacd6043 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -309,7 +309,7 @@ func unwindm(restore *bool) { switch GOARCH { default: throw("unwindm not implemented") - case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "mips", "mipsle": + case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "mips", "mipsle", "riscv64": sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize)) case "arm64": sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16)) diff --git a/src/runtime/tls_riscv64.s b/src/runtime/tls_riscv64.s index 8386980421..22b550b761 100644 --- a/src/runtime/tls_riscv64.s +++ b/src/runtime/tls_riscv64.s @@ -9,10 +9,22 @@ // If !iscgo, this is a no-op. // -// NOTE: mcall() assumes this clobbers only R23 (REGTMP). -// FIXME: cgo +// NOTE: mcall() assumes this clobbers only X31 (REG_TMP). TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 + MOVB runtime·iscgo(SB), X31 + BEQ X0, X31, nocgo + + MOV runtime·tls_g(SB), X31 + ADD X4, X31 // add offset to thread pointer (X4) + MOV g, (X31) + +nocgo: RET TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0 + MOV runtime·tls_g(SB), X31 + ADD X4, X31 // add offset to thread pointer (X4) + MOV (X31), g RET + +GLOBL runtime·tls_g(SB), TLSBSS, $8 -- cgit v1.3 From 8eb846fd37eb7bded8a1cf6932be2c59069863e5 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Wed, 4 Nov 2020 00:26:15 +1100 Subject: cmd/compile,cmd/dist,cmd/go: enable pie buildmode for linux/riscv64 Enable pie as a buildmode for linux/riscv64, along with associated tests. Change-Id: I3fb0234d534dbeb96aa6cee6ae872304fbe02cf4 Reviewed-on: https://go-review.googlesource.com/c/go/+/267317 Trust: Joel Sing Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/dist/test.go | 2 +- src/cmd/go/go_test.go | 2 +- src/cmd/internal/sys/supported.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 8b94c7f71b..d1097e8236 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -130,7 +130,7 @@ func hidePanic() { // supportsDynlink reports whether or not the code generator for the given // architecture supports the -shared and -dynlink flags. func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) } // timing data for compiler phases diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index d12a52b1cc..f087aba2b9 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1021,7 +1021,7 @@ func (t *tester) supportedBuildmode(mode string) bool { case "pie": switch pair { case "aix/ppc64", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "android-amd64", "android-arm", "android-arm64", "android-386": return true case "darwin-amd64", "darwin-arm64": diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 7bbadd3974..91d5884036 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2022,7 +2022,7 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "windows/386", "windows/amd64", "windows/arm": diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index 69d7591440..ef7c017bd4 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 "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", -- cgit v1.3 From 594b4a3bfef24ed207e058fb522ed51e65cb93bf Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Wed, 4 Nov 2020 00:29:39 +1100 Subject: cmd/dist: enable additional cgo tests on openbsd architectures OpenBSD gained __thread support quite some time ago. Change-Id: I7de0a5c0c4de1a7ce59e48ac939fc2daf56be8f5 Reviewed-on: https://go-review.googlesource.com/c/go/+/267318 Trust: Joel Sing Reviewed-by: Cherry Zhang Run-TryBot: Joel Sing TryBot-Result: Go Bot --- src/cmd/dist/test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index f087aba2b9..5e3711b0c8 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1095,7 +1095,6 @@ func (t *tester) cgoTest(dt *distTest) error { pair := gohostos + "-" + goarch switch pair { case "darwin-amd64", "darwin-arm64", - "openbsd-386", "openbsd-amd64", "windows-386", "windows-amd64": // test linkmode=external, but __thread not supported, so skip testtls. if !t.extLink() { @@ -1118,7 +1117,8 @@ func (t *tester) cgoTest(dt *distTest) error { "dragonfly-amd64", "freebsd-386", "freebsd-amd64", "freebsd-arm", "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", - "netbsd-386", "netbsd-amd64": + "netbsd-386", "netbsd-amd64", + "openbsd-386", "openbsd-amd64", "openbsd-arm", "openbsd-arm64", "openbsd-mips64": cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") -- cgit v1.3 From b7e0adfee28b516a0dcceb5ecf0e5a695b38c61f Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Thu, 5 Nov 2020 16:41:32 +1100 Subject: cmd/dist: remove openbsd/mips64 from incomplete ports Remove openbsd/mips64 from incomplete ports lists - all of the necessary code has landed and we want to run tests so we can see/deal with remaining failures. Update #40995 Change-Id: I5d4f89af82ff3abe57570a9a8abf889498093d32 Reviewed-on: https://go-review.googlesource.com/c/go/+/267606 Trust: Joel Sing Reviewed-by: Cherry Zhang --- src/cmd/dist/build.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 20cb04d797..c8c3212d16 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1580,8 +1580,7 @@ var cgoEnabled = map[string]bool{ // List of platforms which are supported but not complete yet. These get // filtered out of cgoEnabled for 'dist list'. See golang.org/issue/28944 var incomplete = map[string]bool{ - "linux/sparc64": true, - "openbsd/mips64": true, + "linux/sparc64": true, } func needCC() bool { -- cgit v1.3 From 5e181357c7dd5cde11b28b2db0b4fb02159ddacf Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Mon, 9 Nov 2020 14:54:55 +0100 Subject: crypto/x509: drop the cgo implementation of root_darwin_amd64.go This code was preserved just to do side-by-side testing while transitioning to the Go implementation. There haven't been mismatch issues, so drop the cgo code, which was making it hard to improve the Go code without diverging. Change-Id: I2a23039c31a46e88b94250aafbc98d4ea8daf22f Reviewed-on: https://go-review.googlesource.com/c/go/+/232397 Run-TryBot: Filippo Valsorda TryBot-Result: Go Bot Trust: Filippo Valsorda Reviewed-by: Brad Fitzpatrick --- src/cmd/dist/test.go | 1 - src/crypto/x509/internal/macos/corefoundation.go | 4 + src/crypto/x509/internal/macos/security.go | 4 + src/crypto/x509/root_cgo_darwin.go | 326 ----------------------- src/crypto/x509/root_darwin.go | 4 - src/crypto/x509/root_darwin_test.go | 33 --- src/crypto/x509/root_ios.go | 3 - src/crypto/x509/root_ios_gen.go | 3 - src/crypto/x509/root_omit.go | 3 - src/go/build/deps_test.go | 2 +- 10 files changed, 9 insertions(+), 374 deletions(-) delete mode 100644 src/crypto/x509/root_cgo_darwin.go (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 5e3711b0c8..2a17ab9cae 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1444,7 +1444,6 @@ func (t *tester) testDirTest(dt *distTest, shard, shards int) error { // cgoPackages is the standard packages that use cgo. var cgoPackages = []string{ - "crypto/x509", "net", "os/user", } diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go index a248ee3292..9b776d4b85 100644 --- a/src/crypto/x509/internal/macos/corefoundation.go +++ b/src/crypto/x509/internal/macos/corefoundation.go @@ -16,6 +16,10 @@ import ( "unsafe" ) +// Core Foundation linker flags for the external linker. See Issue 42459. +//go:cgo_ldflag "-framework" +//go:cgo_ldflag "CoreFoundation" + // CFRef is an opaque reference to a Core Foundation object. It is a pointer, // but to memory not owned by Go, so not an unsafe.Pointer. type CFRef uintptr diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go index 59cc19c587..5e39e93666 100644 --- a/src/crypto/x509/internal/macos/security.go +++ b/src/crypto/x509/internal/macos/security.go @@ -12,6 +12,10 @@ import ( "unsafe" ) +// Security.framework linker flags for the external linker. See Issue 42459. +//go:cgo_ldflag "-framework" +//go:cgo_ldflag "Security" + // Based on https://opensource.apple.com/source/Security/Security-59306.41.2/base/Security.h type SecTrustSettingsResult int32 diff --git a/src/crypto/x509/root_cgo_darwin.go b/src/crypto/x509/root_cgo_darwin.go deleted file mode 100644 index 825e8d4812..0000000000 --- a/src/crypto/x509/root_cgo_darwin.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2011 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 !ios - -package x509 - -// This cgo implementation exists only to support side-by-side testing by -// TestSystemRoots. It can be removed once we are confident in the no-cgo -// implementation. - -/* -#cgo CFLAGS: -mmacosx-version-min=10.11 -#cgo LDFLAGS: -framework CoreFoundation -framework Security - -#include -#include - -#include -#include - -static Boolean isSSLPolicy(SecPolicyRef policyRef) { - if (!policyRef) { - return false; - } - CFDictionaryRef properties = SecPolicyCopyProperties(policyRef); - if (properties == NULL) { - return false; - } - Boolean isSSL = false; - CFTypeRef value = NULL; - if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) { - isSSL = CFEqual(value, kSecPolicyAppleSSL); - } - CFRelease(properties); - return isSSL; -} - -// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value -// for a certificate in the user or admin domain, combining usage constraints -// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and -// kSecTrustSettingsAllowedError. -// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting -static SInt32 sslTrustSettingsResult(SecCertificateRef cert) { - CFArrayRef trustSettings = NULL; - OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings); - - // According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings", - // but the rules of the override are unclear. Let's assume admin trust settings are applicable - // if and only if user trust settings fail to load or are NULL. - if (err != errSecSuccess || trustSettings == NULL) { - if (trustSettings != NULL) CFRelease(trustSettings); - err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings); - } - - // > no trust settings [...] means "this certificate must be verified to a known trusted certificate” - // (Should this cause a fallback from user to admin domain? It's unclear.) - if (err != errSecSuccess || trustSettings == NULL) { - if (trustSettings != NULL) CFRelease(trustSettings); - return kSecTrustSettingsResultUnspecified; - } - - // > An empty trust settings array means "always trust this certificate” with an - // > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot. - if (CFArrayGetCount(trustSettings) == 0) { - CFRelease(trustSettings); - return kSecTrustSettingsResultTrustRoot; - } - - // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"), - // but the Go linker's internal linking mode can't handle CFSTR relocations. - // Create our own dynamic string instead and release it below. - CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString( - NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8); - CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString( - NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8); - CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString( - NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8); - - CFIndex m; SInt32 result = 0; - for (m = 0; m < CFArrayGetCount(trustSettings); m++) { - CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m); - - // First, check if this trust setting is constrained to a non-SSL policy. - SecPolicyRef policyRef; - if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) { - if (!isSSLPolicy(policyRef)) { - continue; - } - } - - if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) { - // Restricted to a hostname, not a root. - continue; - } - - CFNumberRef cfNum; - if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) { - CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result); - } else { - // > If this key is not present, a default value of - // > kSecTrustSettingsResultTrustRoot is assumed. - result = kSecTrustSettingsResultTrustRoot; - } - - // If multiple dictionaries match, we are supposed to "OR" them, - // the semantics of which are not clear. Since TrustRoot and TrustAsRoot - // are mutually exclusive, Deny should probably override, and Invalid and - // Unspecified be overridden, approximate this by stopping at the first - // TrustRoot, TrustAsRoot or Deny. - if (result == kSecTrustSettingsResultTrustRoot) { - break; - } else if (result == kSecTrustSettingsResultTrustAsRoot) { - break; - } else if (result == kSecTrustSettingsResultDeny) { - break; - } - } - - // If trust settings are present, but none of them match the policy... - // the docs don't tell us what to do. - // - // "Trust settings for a given use apply if any of the dictionaries in the - // certificate’s trust settings array satisfies the specified use." suggests - // that it's as if there were no trust settings at all, so we should probably - // fallback to the admin trust settings. TODO. - if (result == 0) { - result = kSecTrustSettingsResultUnspecified; - } - - CFRelease(_kSecTrustSettingsPolicy); - CFRelease(_kSecTrustSettingsPolicyString); - CFRelease(_kSecTrustSettingsResult); - CFRelease(trustSettings); - - return result; -} - -// isRootCertificate reports whether Subject and Issuer match. -static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) { - CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef); - if (*errRef != NULL) { - return false; - } - CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef); - if (*errRef != NULL) { - CFRelease(subjectName); - return false; - } - Boolean equal = CFEqual(subjectName, issuerName); - CFRelease(subjectName); - CFRelease(issuerName); - return equal; -} - -// CopyPEMRoots fetches the system's list of trusted X.509 root certificates -// for the kSecTrustSettingsPolicy SSL. -// -// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root -// certificates of the system. On failure, the function returns -1. -// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots. -// -// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must -// be released (using CFRelease) after we've consumed its content. -static int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) { - int i; - - if (debugDarwinRoots) { - fprintf(stderr, "crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid); - fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot); - fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot); - fprintf(stderr, "crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny); - fprintf(stderr, "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified); - } - - // Get certificates from all domains, not just System, this lets - // the user add CAs to their "login" keychain, and Admins to add - // to the "System" keychain - SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem, - kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser }; - - int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain); - if (pemRoots == NULL || untrustedPemRoots == NULL) { - return -1; - } - - CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); - CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0); - for (i = 0; i < numDomains; i++) { - int j; - CFArrayRef certs = NULL; - OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs); - if (err != noErr) { - continue; - } - - CFIndex numCerts = CFArrayGetCount(certs); - for (j = 0; j < numCerts; j++) { - SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j); - if (cert == NULL) { - continue; - } - - SInt32 result; - if (domains[i] == kSecTrustSettingsDomainSystem) { - // Certs found in the system domain are always trusted. If the user - // configures "Never Trust" on such a cert, it will also be found in the - // admin or user domain, causing it to be added to untrustedPemRoots. The - // Go code will then clean this up. - result = kSecTrustSettingsResultTrustRoot; - } else { - result = sslTrustSettingsResult(cert); - if (debugDarwinRoots) { - CFErrorRef errRef = NULL; - CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef); - if (errRef != NULL) { - fprintf(stderr, "crypto/x509: SecCertificateCopyShortDescription failed\n"); - CFRelease(errRef); - continue; - } - - CFIndex length = CFStringGetLength(summary); - CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - char *buffer = malloc(maxSize); - if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) { - fprintf(stderr, "crypto/x509: %s returned %d\n", buffer, (int)result); - } - free(buffer); - CFRelease(summary); - } - } - - CFMutableDataRef appendTo; - // > Note the distinction between the results kSecTrustSettingsResultTrustRoot - // > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to - // > root (self-signed) certificates; the latter can only be applied to - // > non-root certificates. - if (result == kSecTrustSettingsResultTrustRoot) { - CFErrorRef errRef = NULL; - if (!isRootCertificate(cert, &errRef) || errRef != NULL) { - if (errRef != NULL) CFRelease(errRef); - continue; - } - - appendTo = combinedData; - } else if (result == kSecTrustSettingsResultTrustAsRoot) { - CFErrorRef errRef = NULL; - if (isRootCertificate(cert, &errRef) || errRef != NULL) { - if (errRef != NULL) CFRelease(errRef); - continue; - } - - appendTo = combinedData; - } else if (result == kSecTrustSettingsResultDeny) { - appendTo = combinedUntrustedData; - } else if (result == kSecTrustSettingsResultUnspecified) { - // Certificates with unspecified trust should probably be added to a pool of - // intermediates for chain building, or checked for transitive trust and - // added to the root pool (which is an imprecise approximation because it - // cuts chains short) but we don't support either at the moment. TODO. - continue; - } else { - continue; - } - - CFDataRef data = NULL; - err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); - if (err != noErr) { - continue; - } - if (data != NULL) { - CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data)); - CFRelease(data); - } - } - CFRelease(certs); - } - *pemRoots = combinedData; - *untrustedPemRoots = combinedUntrustedData; - return 0; -} -*/ -import "C" -import ( - "errors" - "unsafe" -) - -func init() { - loadSystemRootsWithCgo = _loadSystemRootsWithCgo -} - -func _loadSystemRootsWithCgo() (*CertPool, error) { - var data, untrustedData C.CFDataRef - err := C.CopyPEMRoots(&data, &untrustedData, C.bool(debugDarwinRoots)) - if err == -1 { - return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo") - } - defer C.CFRelease(C.CFTypeRef(data)) - defer C.CFRelease(C.CFTypeRef(untrustedData)) - - buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) - roots := NewCertPool() - roots.AppendCertsFromPEM(buf) - - if C.CFDataGetLength(untrustedData) == 0 { - return roots, nil - } - - buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData))) - untrustedRoots := NewCertPool() - untrustedRoots.AppendCertsFromPEM(buf) - - trustedRoots := NewCertPool() - for _, lc := range roots.lazyCerts { - c, err := lc.getCert() - if err != nil { - return nil, err - } - if !untrustedRoots.contains(c) { - trustedRoots.AddCert(c) - } - } - return trustedRoots, nil -} diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go index ce88de025e..c9ea7e80f3 100644 --- a/src/crypto/x509/root_darwin.go +++ b/src/crypto/x509/root_darwin.go @@ -20,10 +20,6 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return nil, nil } -// loadSystemRootsWithCgo is set in root_cgo_darwin_amd64.go when cgo is -// available, and is only used for testing. -var loadSystemRootsWithCgo func() (*CertPool, error) - func loadSystemRoots() (*CertPool, error) { var trustedRoots []*Certificate untrustedRoots := make(map[string]bool) diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go index 69f181c2d4..ae2bd02bf8 100644 --- a/src/crypto/x509/root_darwin_test.go +++ b/src/crypto/x509/root_darwin_test.go @@ -28,39 +28,6 @@ func TestSystemRoots(t *testing.T) { t.Errorf("want at least %d system roots, have %d", want, have) } - if loadSystemRootsWithCgo == nil { - t.Skip("cgo not available, can't compare pool") - } - - t1 := time.Now() - cgoRoots, err := loadSystemRootsWithCgo() // cgo roots - cgoSysRootsDuration := time.Since(t1) - - if err != nil { - t.Fatalf("failed to read cgo roots: %v", err) - } - - t.Logf("loadSystemRootsWithCgo: %v", cgoSysRootsDuration) - - // Check that the two cert pools are the same. - sysPool := make(map[string]*Certificate, sysRoots.len()) - for i := 0; i < sysRoots.len(); i++ { - c := sysRoots.mustCert(t, i) - sysPool[string(c.Raw)] = c - } - for i := 0; i < cgoRoots.len(); i++ { - c := cgoRoots.mustCert(t, i) - - if _, ok := sysPool[string(c.Raw)]; ok { - delete(sysPool, string(c.Raw)) - } else { - t.Errorf("certificate only present in cgo pool: %v", c.Subject) - } - } - for _, c := range sysPool { - t.Errorf("certificate only present in real pool: %v", c.Subject) - } - if t.Failed() { cmd := exec.Command("security", "dump-trust-settings") cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr diff --git a/src/crypto/x509/root_ios.go b/src/crypto/x509/root_ios.go index bb4a5f75ba..cb3529d6d5 100644 --- a/src/crypto/x509/root_ios.go +++ b/src/crypto/x509/root_ios.go @@ -10,9 +10,6 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return nil, nil } -// loadSystemRootsWithCgo is not available on iOS. -var loadSystemRootsWithCgo func() (*CertPool, error) - func loadSystemRoots() (*CertPool, error) { p := NewCertPool() p.AppendCertsFromPEM([]byte(systemRootsPEM)) diff --git a/src/crypto/x509/root_ios_gen.go b/src/crypto/x509/root_ios_gen.go index f7eecb576d..2bcdab1a77 100644 --- a/src/crypto/x509/root_ios_gen.go +++ b/src/crypto/x509/root_ios_gen.go @@ -172,9 +172,6 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return nil, nil } -// loadSystemRootsWithCgo is not available on iOS. -var loadSystemRootsWithCgo func() (*CertPool, error) - func loadSystemRoots() (*CertPool, error) { p := NewCertPool() p.AppendCertsFromPEM([]byte(systemRootsPEM)) diff --git a/src/crypto/x509/root_omit.go b/src/crypto/x509/root_omit.go index 175d71643b..0055b3b862 100644 --- a/src/crypto/x509/root_omit.go +++ b/src/crypto/x509/root_omit.go @@ -24,6 +24,3 @@ func loadSystemRoots() (*CertPool, error) { func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { return nil, nil } - -// loadSystemRootsWithCgo is not available on iOS. -var loadSystemRootsWithCgo func() (*CertPool, error) diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index b26b2bd199..bf1367355d 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -395,7 +395,7 @@ var depsRules = ` CGO, net !< CRYPTO-MATH; # TLS, Prince of Dependencies. - CGO, CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem + CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem < golang.org/x/crypto/internal/subtle < golang.org/x/crypto/chacha20 < golang.org/x/crypto/poly1305 -- cgit v1.3 From b9365488f017ba88540f21927a69e34351941db1 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 24 Nov 2020 17:48:38 +0100 Subject: cmd/internal/objabi: assume GOARM=7 on Android CL 34641 changed the Go runtime to assume GOARM=7 support on Android. This change completes that by assuming GOARM=7 in the toolchain, fixing the gotcha of inexplicably slow performance on non-arm64 Android devices. There is already code in cmd/dist to force GOARM to 7 on GOOS=android. However, dist is most likely run with GOOS != android. Change-Id: I5e2bf11c3ecd0f6c193229eaa8ddc570722799d1 Reviewed-on: https://go-review.googlesource.com/c/go/+/272846 Run-TryBot: Elias Naur TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Trust: Elias Naur --- src/cmd/dist/util.go | 6 ------ src/cmd/internal/objabi/util.go | 7 ++++++- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go index 0a419e465f..9b4f8d2dec 100644 --- a/src/cmd/dist/util.go +++ b/src/cmd/dist/util.go @@ -383,12 +383,6 @@ func xsamefile(f1, f2 string) bool { } func xgetgoarm() string { - if goos == "android" { - // Assume all android devices have VFPv3. - // These ports are also mostly cross-compiled, so it makes little - // sense to auto-detect the setting. - return "7" - } if gohostarch != "arm" || goos != gohostos { // Conservative default for cross-compilation. return "5" diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index 9479ab2cd9..d36e743580 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -40,7 +40,12 @@ const ( ) func goarm() int { - switch v := envOr("GOARM", defaultGOARM); v { + def := defaultGOARM + if GOOS == "android" { + // Android devices always support GOARM=7. + def = "7" + } + switch v := envOr("GOARM", def); v { case "5": return 5 case "6": -- cgit v1.3 From ef603bead5d336e81954f890e20efa0261581792 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 25 Nov 2020 19:12:13 +0100 Subject: cmd/dist: restore GOARM=7 default for android/arm Fixes the android/arm builder. Without it, the builder reported unexpected stale targets during bootstrap: https://build.golang.org/log/b951f1171be54cf4a12c2a0720ffaf07f8a11377 Tighten the GOARM=7 default in cmd/internal/objabi while here. Change-Id: I944744910193e72e91bc37b5bf0783076b45e579 Reviewed-on: https://go-review.googlesource.com/c/go/+/273167 Run-TryBot: Elias Naur Reviewed-by: Cherry Zhang TryBot-Result: Go Bot Trust: Elias Naur --- src/cmd/dist/util.go | 6 ++++++ src/cmd/internal/objabi/util.go | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go index 9b4f8d2dec..0a419e465f 100644 --- a/src/cmd/dist/util.go +++ b/src/cmd/dist/util.go @@ -383,6 +383,12 @@ func xsamefile(f1, f2 string) bool { } func xgetgoarm() string { + if goos == "android" { + // Assume all android devices have VFPv3. + // These ports are also mostly cross-compiled, so it makes little + // sense to auto-detect the setting. + return "7" + } if gohostarch != "arm" || goos != gohostos { // Conservative default for cross-compilation. return "5" diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index d36e743580..a73ab479a1 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -41,8 +41,8 @@ const ( func goarm() int { def := defaultGOARM - if GOOS == "android" { - // Android devices always support GOARM=7. + if GOOS == "android" && GOARCH == "arm" { + // Android arm devices always support GOARM=7. def = "7" } switch v := envOr("GOARM", def); v { -- cgit v1.3 From 7430266af4f951df3c113f2c817bc600650e2295 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 21 Nov 2020 21:33:18 -0500 Subject: cmd/internal/codesign: new package On macOS/ARM64, the kernel requires that binaries must have a valid code signature to run. The C toolchain code-signs the binary at link time. We do the same. It is more subtle for Go because we stamp the buildid after linking. As the signature contains hashes of the entire file (except the signature itself), we must (re)generate the signature after stamping the buildid. This CL adds a new codesign package, which provides functionality to generate the code signature. It is a separate internal package so it can be used both in the linker and by the go command. The next CLs will add code-signing to the linker and the go command. Updates #38485, #42684. Change-Id: Id46801a6665beebaab0eb413ff2e64c5b9467059 Reviewed-on: https://go-review.googlesource.com/c/go/+/272254 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Austin Clements Reviewed-by: Than McIntosh --- src/cmd/dist/buildtool.go | 1 + src/cmd/internal/codesign/codesign.go | 268 ++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 src/cmd/internal/codesign/codesign.go (limited to 'src/cmd/dist') diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 37b3d45977..e7bedfb84e 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -53,6 +53,7 @@ var bootstrapDirs = []string{ "cmd/compile/internal/x86", "cmd/compile/internal/wasm", "cmd/internal/bio", + "cmd/internal/codesign", "cmd/internal/gcprog", "cmd/internal/dwarf", "cmd/internal/edit", diff --git a/src/cmd/internal/codesign/codesign.go b/src/cmd/internal/codesign/codesign.go new file mode 100644 index 0000000000..0517a10640 --- /dev/null +++ b/src/cmd/internal/codesign/codesign.go @@ -0,0 +1,268 @@ +// 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 codesign provides basic functionalities for +// ad-hoc code signing of Mach-O files. +// +// This is not a general tool for code-signing. It is made +// specifically for the Go toolchain. It uses the same +// ad-hoc signing algorithm as the Darwin linker. +package codesign + +import ( + "crypto/sha256" + "debug/macho" + "encoding/binary" + "io" +) + +// Code signature layout. +// +// The code signature is a block of bytes that contains +// a SuperBlob, which contains one or more Blobs. For ad-hoc +// signing, a single CodeDirectory Blob suffices. +// +// A SuperBlob starts with its header (the binary representation +// of the SuperBlob struct), followed by a list of (in our case, +// one) Blobs (offset and size). A CodeDirectory Blob starts +// with its head (the binary representation of CodeDirectory struct), +// followed by the identifier (as a C string) and the hashes, at +// the corresponding offsets. +// +// The signature data must be included in the __LINKEDIT segment. +// In the Mach-O file header, an LC_CODE_SIGNATURE load command +// points to the data. + +const ( + pageSizeBits = 12 + pageSize = 1 << pageSizeBits +) + +const LC_CODE_SIGNATURE = 0x1d + +// Constants and struct layouts are from +// https://opensource.apple.com/source/xnu/xnu-4903.270.47/osfmk/kern/cs_blobs.h + +const ( + CSMAGIC_REQUIREMENT = 0xfade0c00 // single Requirement blob + CSMAGIC_REQUIREMENTS = 0xfade0c01 // Requirements vector (internal requirements) + CSMAGIC_CODEDIRECTORY = 0xfade0c02 // CodeDirectory blob + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures + + CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory +) + +const ( + CS_HASHTYPE_SHA1 = 1 + CS_HASHTYPE_SHA256 = 2 + CS_HASHTYPE_SHA256_TRUNCATED = 3 + CS_HASHTYPE_SHA384 = 4 +) + +const ( + CS_EXECSEG_MAIN_BINARY = 0x1 // executable segment denotes main binary + CS_EXECSEG_ALLOW_UNSIGNED = 0x10 // allow unsigned pages (for debugging) + CS_EXECSEG_DEBUGGER = 0x20 // main binary is debugger + CS_EXECSEG_JIT = 0x40 // JIT enabled + CS_EXECSEG_SKIP_LV = 0x80 // skip library validation + CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 // can bless cdhash for execution + CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 // can execute blessed cdhash +) + +type Blob struct { + typ uint32 // type of entry + offset uint32 // offset of entry + // data follows +} + +func (b *Blob) put(out []byte) []byte { + out = put32be(out, b.typ) + out = put32be(out, b.offset) + return out +} + +const blobSize = 2 * 4 + +type SuperBlob struct { + magic uint32 // magic number + length uint32 // total length of SuperBlob + count uint32 // number of index entries following + // blobs []Blob +} + +func (s *SuperBlob) put(out []byte) []byte { + out = put32be(out, s.magic) + out = put32be(out, s.length) + out = put32be(out, s.count) + return out +} + +const superBlobSize = 3 * 4 + +type CodeDirectory struct { + magic uint32 // magic number (CSMAGIC_CODEDIRECTORY) + length uint32 // total length of CodeDirectory blob + version uint32 // compatibility version + flags uint32 // setup and mode flags + hashOffset uint32 // offset of hash slot element at index zero + identOffset uint32 // offset of identifier string + nSpecialSlots uint32 // number of special hash slots + nCodeSlots uint32 // number of ordinary (code) hash slots + codeLimit uint32 // limit to main image signature range + hashSize uint8 // size of each hash in bytes + hashType uint8 // type of hash (cdHashType* constants) + _pad1 uint8 // unused (must be zero) + pageSize uint8 // log2(page size in bytes); 0 => infinite + _pad2 uint32 // unused (must be zero) + scatterOffset uint32 + teamOffset uint32 + _pad3 uint32 + codeLimit64 uint64 + execSegBase uint64 + execSegLimit uint64 + execSegFlags uint64 + // data follows +} + +func (c *CodeDirectory) put(out []byte) []byte { + out = put32be(out, c.magic) + out = put32be(out, c.length) + out = put32be(out, c.version) + out = put32be(out, c.flags) + out = put32be(out, c.hashOffset) + out = put32be(out, c.identOffset) + out = put32be(out, c.nSpecialSlots) + out = put32be(out, c.nCodeSlots) + out = put32be(out, c.codeLimit) + out = put8(out, c.hashSize) + out = put8(out, c.hashType) + out = put8(out, c._pad1) + out = put8(out, c.pageSize) + out = put32be(out, c._pad2) + out = put32be(out, c.scatterOffset) + out = put32be(out, c.teamOffset) + out = put32be(out, c._pad3) + out = put64be(out, c.codeLimit64) + out = put64be(out, c.execSegBase) + out = put64be(out, c.execSegLimit) + out = put64be(out, c.execSegFlags) + return out +} + +const codeDirectorySize = 13*4 + 4 + 4*8 + +// CodeSigCmd is Mach-O LC_CODE_SIGNATURE load command. +type CodeSigCmd struct { + Cmd uint32 // LC_CODE_SIGNATURE + Cmdsize uint32 // sizeof this command (16) + Dataoff uint32 // file offset of data in __LINKEDIT segment + Datasize uint32 // file size of data in __LINKEDIT segment +} + +func FindCodeSigCmd(f *macho.File) (CodeSigCmd, bool) { + get32 := f.ByteOrder.Uint32 + for _, l := range f.Loads { + data := l.Raw() + cmd := get32(data) + if cmd == LC_CODE_SIGNATURE { + return CodeSigCmd{ + cmd, + get32(data[4:]), + get32(data[8:]), + get32(data[12:]), + }, true + } + } + return CodeSigCmd{}, false +} + +func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] } +func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] } +func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] } +func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] } + +// Size computes the size of the code signature. +// id is the identifier used for signing (a field in CodeDirectory blob, which +// has no significance in ad-hoc signing). +func Size(codeSize int64, id string) int64 { + nhashes := (codeSize + pageSize - 1) / pageSize + idOff := int64(codeDirectorySize) + hashOff := idOff + int64(len(id)+1) + cdirSz := hashOff + nhashes*sha256.Size + return int64(superBlobSize+blobSize) + cdirSz +} + +// Sign generates an ad-hoc code signature and writes it to out. +// out must have length at least Size(codeSize, id). +// data is the file content without the signature, of size codeSize. +// textOff and textSize is the file offset and size of the text segment. +// isMain is true if this is a main executable. +// id is the identifier used for signing (a field in CodeDirectory blob, which +// has no significance in ad-hoc signing). +func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int64, isMain bool) { + nhashes := (codeSize + pageSize - 1) / pageSize + idOff := int64(codeDirectorySize) + hashOff := idOff + int64(len(id)+1) + sz := Size(codeSize, id) + + // emit blob headers + sb := SuperBlob{ + magic: CSMAGIC_EMBEDDED_SIGNATURE, + length: uint32(sz), + count: 1, + } + blob := Blob{ + typ: CSSLOT_CODEDIRECTORY, + offset: superBlobSize + blobSize, + } + cdir := CodeDirectory{ + magic: CSMAGIC_CODEDIRECTORY, + length: uint32(sz) - (superBlobSize + blobSize), + version: 0x20400, + flags: 0x20002, // adhoc | linkerSigned + hashOffset: uint32(hashOff), + identOffset: uint32(idOff), + nCodeSlots: uint32(nhashes), + codeLimit: uint32(codeSize), + hashSize: sha256.Size, + hashType: CS_HASHTYPE_SHA256, + pageSize: uint8(pageSizeBits), + execSegBase: uint64(textOff), + execSegLimit: uint64(textSize), + } + if isMain { + cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY + } + + outp := out + outp = sb.put(outp) + outp = blob.put(outp) + outp = cdir.put(outp) + + // emit the identifier + outp = puts(outp, []byte(id+"\000")) + + // emit hashes + var buf [pageSize]byte + h := sha256.New() + p := 0 + for p < int(codeSize) { + n, err := io.ReadFull(data, buf[:]) + if err == io.EOF { + break + } + if err != nil && err != io.ErrUnexpectedEOF { + panic(err) + } + if p+n > int(codeSize) { + n = int(codeSize) - p + } + p += n + h.Reset() + h.Write(buf[:n]) + b := h.Sum(nil) + outp = puts(outp, b[:]) + } +} -- cgit v1.3 From c32140fa94cfc51a2152855825f57e27ae3ba133 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 4 Nov 2020 18:20:17 -0500 Subject: all: update to use filepath.WalkDir instead of filepath.Walk Now that filepath.WalkDir is available, it is more efficient and should be used in place of filepath.Walk. Update the tree to reflect best practices. As usual, the code compiled with Go 1.4 during bootstrap is excluded. (In this CL, that's only cmd/dist.) For #42027. Change-Id: Ib0f7b1e43e50b789052f9835a63ced701d8c411c Reviewed-on: https://go-review.googlesource.com/c/go/+/267719 Trust: Russ Cox Run-TryBot: Russ Cox TryBot-Result: Go Bot Reviewed-by: Rob Pike --- src/cmd/compile/fmt_test.go | 3 ++- src/cmd/dist/test.go | 1 + src/cmd/fix/main.go | 6 +++--- src/cmd/go/go_test.go | 9 +++------ src/cmd/go/internal/modfetch/fetch.go | 9 +++++---- src/cmd/go/internal/version/version.go | 12 ++++++++++-- src/cmd/go/testdata/addmod.go | 4 ++-- src/cmd/go/testdata/savedir.go | 4 ++-- src/cmd/gofmt/gofmt.go | 6 +++--- src/cmd/gofmt/long_test.go | 17 +++++++++++++---- src/cmd/internal/moddeps/moddeps_test.go | 2 +- src/compress/gzip/issue14937_test.go | 2 +- src/go/build/deps_test.go | 6 +++--- src/go/doc/headscan.go | 4 ++-- src/index/suffixarray/suffixarray_test.go | 2 +- test/run.go | 17 ++++++++++------- test/winbatch.go | 6 +++--- 17 files changed, 65 insertions(+), 45 deletions(-) (limited to 'src/cmd/dist') diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index e372259c78..6625ccf5e2 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -52,6 +52,7 @@ import ( "go/types" "internal/testenv" "io" + "io/fs" "io/ioutil" "log" "os" @@ -89,7 +90,7 @@ func TestFormats(t *testing.T) { testenv.MustHaveGoBuild(t) // more restrictive than necessary, but that's ok // process all directories - filepath.Walk(".", func(path string, info os.FileInfo, err error) error { + filepath.WalkDir(".", func(path string, info fs.DirEntry, err error) error { if info.IsDir() { if info.Name() == "testdata" { return filepath.SkipDir diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 2a17ab9cae..955ce2a063 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1509,6 +1509,7 @@ func (t *tester) makeGOROOTUnwritable() (undo func()) { } gocacheSubdir, _ := filepath.Rel(dir, gocache) + // Note: Can't use WalkDir here, because this has to compile with Go 1.4. filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if suffix := strings.TrimPrefix(path, dir+string(filepath.Separator)); suffix != "" { if suffix == gocacheSubdir { diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go index 1cea9a876a..1cedf992cf 100644 --- a/src/cmd/fix/main.go +++ b/src/cmd/fix/main.go @@ -234,10 +234,10 @@ func report(err error) { } func walkDir(path string) { - filepath.Walk(path, visitFile) + filepath.WalkDir(path, visitFile) } -func visitFile(path string, f fs.FileInfo, err error) error { +func visitFile(path string, f fs.DirEntry, err error) error { if err == nil && isGoFile(f) { err = processFile(path, false) } @@ -247,7 +247,7 @@ func visitFile(path string, f fs.FileInfo, err error) error { return nil } -func isGoFile(f fs.FileInfo) bool { +func isGoFile(f fs.DirEntry) bool { // ignore non-Go files name := f.Name() return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index a02231fa98..a730c87f97 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -774,7 +774,7 @@ func (tg *testgoData) cleanup() { func removeAll(dir string) error { // module cache has 0444 directories; // make them writable in order to remove content. - filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { + filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error { // chmod not only directories, but also things that we couldn't even stat // due to permission errors: they may also be unreadable directories. if err != nil || info.IsDir() { @@ -820,8 +820,8 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { } { srcdir := filepath.Join(testGOROOT, copydir) tg.tempDir(filepath.Join("goroot", copydir)) - err := filepath.Walk(srcdir, - func(path string, info fs.FileInfo, err error) error { + err := filepath.WalkDir(srcdir, + func(path string, info fs.DirEntry, err error) error { if err != nil { return err } @@ -838,9 +838,6 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { return err } tg.tempFile(dest, string(data)) - if err := os.Chmod(tg.path(dest), info.Mode()|0200); err != nil { - return err - } return nil }) if err != nil { diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go index a3e2cd1f9d..2ee78de5b2 100644 --- a/src/cmd/go/internal/modfetch/fetch.go +++ b/src/cmd/go/internal/modfetch/fetch.go @@ -318,9 +318,10 @@ func makeDirsReadOnly(dir string) { mode fs.FileMode } var dirs []pathMode // in lexical order - filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { - if err == nil && info.Mode()&0222 != 0 { - if info.IsDir() { + filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err == nil && d.IsDir() { + info, err := d.Info() + if err == nil && info.Mode()&0222 != 0 { dirs = append(dirs, pathMode{path, info.Mode()}) } } @@ -337,7 +338,7 @@ func makeDirsReadOnly(dir string) { // any permission changes needed to do so. func RemoveAll(dir string) error { // Module cache has 0555 directories; make them writable in order to remove content. - filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { + filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error { if err != nil { return nil // ignore errors walking in file system } diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go index 44ac24c62d..58cbd32e78 100644 --- a/src/cmd/go/internal/version/version.go +++ b/src/cmd/go/internal/version/version.go @@ -88,8 +88,15 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) { // scanDir scans a directory for executables to run scanFile on. func scanDir(dir string) { - filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { - if info.Mode().IsRegular() || info.Mode()&fs.ModeSymlink != 0 { + filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if d.Type().IsRegular() || d.Type()&fs.ModeSymlink != 0 { + info, err := d.Info() + if err != nil { + if *versionV { + fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) + } + return nil + } scanFile(path, info, *versionV) } return nil @@ -120,6 +127,7 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) { } info = i } + if !isExe(file, info) { if mustPrint { fmt.Fprintf(os.Stderr, "%s: not executable file\n", file) diff --git a/src/cmd/go/testdata/addmod.go b/src/cmd/go/testdata/addmod.go index d1b6467c5d..71ac47fdc1 100644 --- a/src/cmd/go/testdata/addmod.go +++ b/src/cmd/go/testdata/addmod.go @@ -122,8 +122,8 @@ func main() { {Name: ".info", Data: info}, } dir = filepath.Clean(dir) - err = filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { - if !info.Mode().IsRegular() { + err = filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error { + if !info.Type().IsRegular() { return nil } name := info.Name() diff --git a/src/cmd/go/testdata/savedir.go b/src/cmd/go/testdata/savedir.go index 04902df61e..75895ee279 100644 --- a/src/cmd/go/testdata/savedir.go +++ b/src/cmd/go/testdata/savedir.go @@ -49,7 +49,7 @@ func main() { a := new(txtar.Archive) dir = filepath.Clean(dir) - filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { + filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error { if path == dir { return nil } @@ -60,7 +60,7 @@ func main() { } return nil } - if !info.Mode().IsRegular() { + if !info.Type().IsRegular() { return nil } data, err := ioutil.ReadFile(path) diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index dba2411eed..719c681a3e 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -74,7 +74,7 @@ func initParserMode() { } } -func isGoFile(f fs.FileInfo) bool { +func isGoFile(f fs.DirEntry) bool { // ignore non-Go files name := f.Name() return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") @@ -164,7 +164,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error return err } -func visitFile(path string, f fs.FileInfo, err error) error { +func visitFile(path string, f fs.DirEntry, err error) error { if err == nil && isGoFile(f) { err = processFile(path, nil, os.Stdout, false) } @@ -177,7 +177,7 @@ func visitFile(path string, f fs.FileInfo, err error) error { } func walkDir(path string) { - filepath.Walk(path, visitFile) + filepath.WalkDir(path, visitFile) } func main() { diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go index 28306ce83e..4a821705f1 100644 --- a/src/cmd/gofmt/long_test.go +++ b/src/cmd/gofmt/long_test.go @@ -108,12 +108,12 @@ func testFiles(t *testing.T, filenames <-chan string, done chan<- int) { func genFilenames(t *testing.T, filenames chan<- string) { defer close(filenames) - handleFile := func(filename string, fi fs.FileInfo, err error) error { + handleFile := func(filename string, d fs.DirEntry, err error) error { if err != nil { t.Error(err) return nil } - if isGoFile(fi) { + if isGoFile(d) { filenames <- filename nfiles++ } @@ -124,13 +124,13 @@ func genFilenames(t *testing.T, filenames chan<- string) { if *files != "" { for _, filename := range strings.Split(*files, ",") { fi, err := os.Stat(filename) - handleFile(filename, fi, err) + handleFile(filename, &statDirEntry{fi}, err) } return // ignore files under -root } // otherwise, test all Go files under *root - filepath.Walk(*root, handleFile) + filepath.WalkDir(*root, handleFile) } func TestAll(t *testing.T) { @@ -164,3 +164,12 @@ func TestAll(t *testing.T) { fmt.Printf("processed %d files\n", nfiles) } } + +type statDirEntry struct { + info fs.FileInfo +} + +func (d *statDirEntry) Name() string { return d.info.Name() } +func (d *statDirEntry) IsDir() bool { return d.info.IsDir() } +func (d *statDirEntry) Type() fs.FileMode { return d.info.Mode().Type() } +func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil } diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index 7362e7868b..9ea21873c5 100644 --- a/src/cmd/internal/moddeps/moddeps_test.go +++ b/src/cmd/internal/moddeps/moddeps_test.go @@ -33,7 +33,7 @@ func findGorootModules(t *testing.T) []gorootModule { goBin := testenv.GoToolPath(t) goroot.once.Do(func() { - goroot.err = filepath.Walk(runtime.GOROOT(), func(path string, info fs.FileInfo, err error) error { + goroot.err = filepath.WalkDir(runtime.GOROOT(), func(path string, info fs.DirEntry, err error) error { if err != nil { return err } diff --git a/src/compress/gzip/issue14937_test.go b/src/compress/gzip/issue14937_test.go index 24db3641aa..20da0b6824 100644 --- a/src/compress/gzip/issue14937_test.go +++ b/src/compress/gzip/issue14937_test.go @@ -31,7 +31,7 @@ func TestGZIPFilesHaveZeroMTimes(t *testing.T) { t.Fatal("error evaluating GOROOT: ", err) } var files []string - err = filepath.Walk(goroot, func(path string, info fs.FileInfo, err error) error { + err = filepath.WalkDir(goroot, func(path string, info fs.DirEntry, err error) error { if err != nil { return err } diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index bf1367355d..e9ed26aa5f 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -510,8 +510,8 @@ func listStdPkgs(goroot string) ([]string, error) { var pkgs []string src := filepath.Join(goroot, "src") + string(filepath.Separator) - walkFn := func(path string, fi fs.FileInfo, err error) error { - if err != nil || !fi.IsDir() || path == src { + walkFn := func(path string, d fs.DirEntry, err error) error { + if err != nil || !d.IsDir() || path == src { return nil } @@ -528,7 +528,7 @@ func listStdPkgs(goroot string) ([]string, error) { pkgs = append(pkgs, strings.TrimPrefix(name, "vendor/")) return nil } - if err := filepath.Walk(src, walkFn); err != nil { + if err := filepath.WalkDir(src, walkFn); err != nil { return nil, err } return pkgs, nil diff --git a/src/go/doc/headscan.go b/src/go/doc/headscan.go index 8ea462366e..fe26a0ea84 100644 --- a/src/go/doc/headscan.go +++ b/src/go/doc/headscan.go @@ -69,8 +69,8 @@ func main() { flag.Parse() fset := token.NewFileSet() nheadings := 0 - err := filepath.Walk(*root, func(path string, fi fs.FileInfo, err error) error { - if !fi.IsDir() { + err := filepath.WalkDir(*root, func(path string, info fs.DirEntry, err error) error { + if !info.IsDir() { return nil } pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments) diff --git a/src/index/suffixarray/suffixarray_test.go b/src/index/suffixarray/suffixarray_test.go index b6a81123b7..a11a98dae0 100644 --- a/src/index/suffixarray/suffixarray_test.go +++ b/src/index/suffixarray/suffixarray_test.go @@ -503,7 +503,7 @@ func makeText(name string) ([]byte, error) { return nil, err } case "go": - err := filepath.Walk("../..", func(path string, info fs.FileInfo, err error) error { + err := filepath.WalkDir("../..", func(path string, info fs.DirEntry, err error) error { if err == nil && strings.HasSuffix(path, ".go") && !info.IsDir() { file, err := ioutil.ReadFile(path) if err != nil { diff --git a/test/run.go b/test/run.go index 672861c8d7..4abf32d25c 100644 --- a/test/run.go +++ b/test/run.go @@ -14,6 +14,7 @@ import ( "fmt" "hash/fnv" "io" + "io/fs" "io/ioutil" "log" "os" @@ -1793,7 +1794,7 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + return filepath.WalkDir(srcRoot, func(srcPath string, d fs.DirEntry, err error) error { if err != nil || srcPath == srcRoot { return err } @@ -1804,14 +1805,16 @@ func overlayDir(dstRoot, srcRoot string) error { } dstPath := filepath.Join(dstRoot, suffix) - perm := info.Mode() & os.ModePerm - if info.Mode()&os.ModeSymlink != 0 { + var info fs.FileInfo + if d.Type()&os.ModeSymlink != 0 { info, err = os.Stat(srcPath) - if err != nil { - return err - } - perm = info.Mode() & os.ModePerm + } else { + info, err = d.Info() } + if err != nil { + return err + } + perm := info.Mode() & os.ModePerm // Always copy directories (don't symlink them). // If we add a file in the overlay, we don't want to add it in the original. diff --git a/test/winbatch.go b/test/winbatch.go index c3b48d385c..54c2fff134 100644 --- a/test/winbatch.go +++ b/test/winbatch.go @@ -27,11 +27,11 @@ func main() { // Walk the entire Go repository source tree (without GOROOT/pkg), // skipping directories that start with "." and named "testdata", // and ensure all .bat files found have exact CRLF line endings. - err := filepath.Walk(runtime.GOROOT(), func(path string, fi os.FileInfo, err error) error { + err := filepath.WalkDir(runtime.GOROOT(), func(path string, d os.DirEntry, err error) error { if err != nil { return err } - if fi.IsDir() && (strings.HasPrefix(fi.Name(), ".") || fi.Name() == "testdata") { + if d.IsDir() && (strings.HasPrefix(d.Name(), ".") || d.Name() == "testdata") { return filepath.SkipDir } if path == filepath.Join(runtime.GOROOT(), "pkg") { @@ -39,7 +39,7 @@ func main() { // Skip it to avoid false positives. (Also see golang.org/issue/37929.) return filepath.SkipDir } - if filepath.Ext(fi.Name()) == ".bat" { + if filepath.Ext(d.Name()) == ".bat" { enforceBatchStrictCRLF(path) } return nil -- cgit v1.3