From: Ian Lance Taylor Date: Tue, 9 May 2017 21:34:16 +0000 (-0700) Subject: cmd/internal/obj, cmd/link: fix st_other field on PPC64 X-Git-Tag: go1.9beta1~295 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=5331e7e9df017374a05a66497fd367e165b8aaf5;p=gostls13.git cmd/internal/obj, cmd/link: fix st_other field on PPC64 In PPC64 ELF files, the st_other field indicates the number of prologue instructions between the global and local entry points. We add the instructions in the compiler and assembler if -shared is used. We were assuming that the instructions were present when building a c-archive or PIE or doing dynamic linking, on the assumption that those are the cases where the go tool would be building with -shared. That assumption fails when using some other tool, such as Bazel, that does not necessarily use -shared in exactly the same way. This CL records in the object file whether a symbol was compiled with -shared (this will be the same for all symbols in a given compilation) and uses that information when setting the st_other field. Fixes #20290. Change-Id: Ib2b77e16aef38824871102e3c244fcf04a86c6ea Reviewed-on: https://go-review.googlesource.com/43051 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Michael Hudson-Doyle Reviewed-by: Matthew Dempsky --- diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go index 159d4f8300..dbde92d744 100644 --- a/misc/cgo/testcarchive/carchive_test.go +++ b/misc/cgo/testcarchive/carchive_test.go @@ -218,15 +218,7 @@ func TestEarlySignalHandler(t *testing.T) { } func TestSignalForwarding(t *testing.T) { - switch GOOS { - case "darwin": - switch GOARCH { - case "arm", "arm64": - t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) - } - case "windows": - t.Skip("skipping signal test on Windows") - } + checkSignalForwardingTest(t) defer func() { os.Remove("libgo2.a") @@ -251,51 +243,19 @@ func TestSignalForwarding(t *testing.T) { cmd = exec.Command(bin[0], append(bin[1:], "1")...) out, err := cmd.CombinedOutput() - - if err == nil { - t.Logf("%s", out) - t.Error("test program succeeded unexpectedly") - } else if ee, ok := err.(*exec.ExitError); !ok { - t.Logf("%s", out) - t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) - } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { - t.Logf("%s", out) - t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) - } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV { - t.Logf("%s", out) - t.Errorf("got %v; expected SIGSEGV", ee) - } + t.Logf("%s", out) + expectSignal(t, err, syscall.SIGSEGV) // Test SIGPIPE forwarding cmd = exec.Command(bin[0], append(bin[1:], "3")...) out, err = cmd.CombinedOutput() - - if err == nil { - t.Logf("%s", out) - t.Error("test program succeeded unexpectedly") - } else if ee, ok := err.(*exec.ExitError); !ok { - t.Logf("%s", out) - t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) - } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { - t.Logf("%s", out) - t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) - } else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE { - t.Logf("%s", out) - t.Errorf("got %v; expected SIGPIPE", ee) - } + t.Logf("%s", out) + expectSignal(t, err, syscall.SIGPIPE) } func TestSignalForwardingExternal(t *testing.T) { - switch GOOS { - case "darwin": - switch GOARCH { - case "arm", "arm64": - t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) - } - case "windows": - t.Skip("skipping signal test on Windows") - } + checkSignalForwardingTest(t) defer func() { os.Remove("libgo2.a") @@ -363,14 +323,7 @@ func TestSignalForwardingExternal(t *testing.T) { continue } - if ee, ok := err.(*exec.ExitError); !ok { - t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) - } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { - t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) - } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV { - t.Errorf("got %v; expected SIGSEGV", ee) - } else { - // We got the error we expected. + if expectSignal(t, err, syscall.SIGSEGV) { return } } @@ -378,6 +331,38 @@ func TestSignalForwardingExternal(t *testing.T) { t.Errorf("program succeeded unexpectedly %d times", tries) } +// checkSignalForwardingTest calls t.Skip if the SignalForwarding test +// doesn't work on this platform. +func checkSignalForwardingTest(t *testing.T) { + switch GOOS { + case "darwin": + switch GOARCH { + case "arm", "arm64": + t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) + } + case "windows": + t.Skip("skipping signal test on Windows") + } +} + +// expectSignal checks that err, the exit status of a test program, +// shows a failure due to a specific signal. Returns whether we found +// the expected signal. +func expectSignal(t *testing.T, err error, sig syscall.Signal) bool { + if err == nil { + t.Error("test program succeeded unexpectedly") + } else if ee, ok := err.(*exec.ExitError); !ok { + t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) + } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { + t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) + } else if !ws.Signaled() || ws.Signal() != sig { + t.Errorf("got %v; expected signal %v", ee, sig) + } else { + return true + } + return false +} + func TestOsSignal(t *testing.T) { switch GOOS { case "windows": @@ -592,3 +577,44 @@ func TestSIGPROF(t *testing.T) { t.Fatal(err) } } + +// TestCompileWithoutShared tests that if we compile code without the +// -shared option, we can put it into an archive. When we use the go +// tool with -buildmode=c-archive, it passes -shared to the compiler, +// so we override that. The go tool doesn't work this way, but Bazel +// will likely do it in the future. And it ought to work. This test +// was added because at one time it did not work on PPC GNU/Linux. +func TestCompileWithoutShared(t *testing.T) { + // For simplicity, reuse the signal forwarding test. + checkSignalForwardingTest(t) + + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + }() + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "libgo2") + cmd.Env = gopathEnv + t.Log(cmd.Args) + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err != nil { + t.Fatal(err) + } + + exe := "./testnoshared" + exeSuffix + ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a") + t.Log(ccArgs) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%s", out) + if err != nil { + t.Fatal(err) + } + defer os.Remove(exe) + + binArgs := append(cmdToRun(exe), "3") + t.Log(binArgs) + out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() + t.Logf("%s", out) + expectSignal(t, err, syscall.SIGPIPE) +} diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go index b68fe02eb3..587274401b 100644 --- a/src/cmd/internal/goobj/read.go +++ b/src/cmd/internal/goobj/read.go @@ -532,7 +532,7 @@ func (r *objReader) parseObject(prefix []byte) error { f.Args = r.readInt() f.Frame = r.readInt() flags := r.readInt() - f.Leaf = flags&1 != 0 + f.Leaf = flags&(1<<0) != 0 f.NoSplit = r.readInt() != 0 f.Var = make([]Var, r.readInt()) for i := range f.Var { diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index c550d43f26..dc22eacdf4 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -338,6 +338,9 @@ func (w *objWriter) writeSym(s *LSym) { if s.ReflectMethod() { flags |= 1 << 2 } + if ctxt.Flag_shared { + flags |= 1 << 3 + } w.writeInt(flags) w.writeInt(int64(len(s.Func.Autom))) for _, a := range s.Func.Autom { diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go index 72e02d39cf..b1509e3813 100644 --- a/src/cmd/internal/obj/ppc64/obj9.go +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -476,6 +476,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // generate the addis instruction except as part of the // load of a large constant, and in that case there is no // way to use r12 as the source. + // + // Note that the same condition is tested in + // putelfsym in cmd/link/internal/ld/symtab.go + // where we set the st_other field to indicate + // the presence of these instructions. q = obj.Appendp(q, c.newprog) q.As = AWORD q.Pos = p.Pos diff --git a/src/cmd/internal/objabi/doc.go b/src/cmd/internal/objabi/doc.go index 7b93fc19e5..dc37817a61 100644 --- a/src/cmd/internal/objabi/doc.go +++ b/src/cmd/internal/objabi/doc.go @@ -77,6 +77,7 @@ // 1<<0 leaf // 1<<1 C function // 1<<2 function may call reflect.Type.Method +// 1<<3 function compiled with -shared // - nlocal [int] // - local [nlocal automatics] // - pcln [pcln table] diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 089b4d3d3c..6a1a47e213 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -135,6 +135,9 @@ const ( // AttrMakeTypelink Amarks types that should be added to the typelink // table. See typelinks.go:typelinks(). AttrMakeTypelink + // AttrShared marks symbols compiled with the -shared option. + AttrShared + // 14 attributes defined so far. ) func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 } @@ -150,6 +153,7 @@ func (a Attribute) OnList() bool { return a&AttrOnList != 0 } func (a Attribute) Local() bool { return a&AttrLocal != 0 } func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 } func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 } +func (a Attribute) Shared() bool { return a&AttrShared != 0 } func (a Attribute) CgoExport() bool { return a.CgoExportDynamic() || a.CgoExportStatic() diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index c91fe28458..13dde21809 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -258,6 +258,9 @@ overwrite: if flags&(1<<2) != 0 { s.Attr |= AttrReflectMethod } + if flags&(1<<3) != 0 { + s.Attr |= AttrShared + } n := r.readInt() pc.Autom = r.autom[:n:n] if !isdup { diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index a35ece13ad..78e9dc26bc 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -147,10 +147,13 @@ func putelfsym(ctxt *Link, x *Symbol, s string, t SymbolType, addr int64, go_ *S if x.Type&SHIDDEN != 0 { other = STV_HIDDEN } - if (Buildmode == BuildmodeCArchive || Buildmode == BuildmodePIE || ctxt.DynlinkingGo()) && SysArch.Family == sys.PPC64 && typ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" { + if SysArch.Family == sys.PPC64 && typ == STT_FUNC && x.Attr.Shared() && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" { // On ppc64 the top three bits of the st_other field indicate how // many instructions separate the global and local entry points. In // our case it is two instructions, indicated by the value 3. + // The conditions here match those in preprocess in + // cmd/internal/obj/ppc64/obj9.go, which is where the + // instructions are inserted. other |= 3 << 5 }