]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj, cmd/link: fix st_other field on PPC64
authorIan Lance Taylor <iant@golang.org>
Tue, 9 May 2017 21:34:16 +0000 (14:34 -0700)
committerIan Lance Taylor <iant@golang.org>
Tue, 9 May 2017 23:36:51 +0000 (23:36 +0000)
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 <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Hudson-Doyle <michael.hudson@canonical.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
misc/cgo/testcarchive/carchive_test.go
src/cmd/internal/goobj/read.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/ppc64/obj9.go
src/cmd/internal/objabi/doc.go
src/cmd/link/internal/ld/link.go
src/cmd/link/internal/ld/objfile.go
src/cmd/link/internal/ld/symtab.go

index 159d4f83007a4c82b1b45de34560ae1732bbfa08..dbde92d7446fca5912ad1954e7366a666250e2da 100644 (file)
@@ -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)
+}
index b68fe02eb35e09e168c55dfd38487c2b29e15a38..587274401b8aef798d087e4b287d98243fce9662 100644 (file)
@@ -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 {
index c550d43f26e6abdd3e39319a8f2b96d2800e3d7a..dc22eacdf48bfe75f94bfcb2546b3c8a7fe67313 100644 (file)
@@ -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 {
index 72e02d39cf4cb5f5876a4d6695022aafc3fb5316..b1509e3813b0414fe3fa8feba0166975b9144670 100644 (file)
@@ -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
index 7b93fc19e57827d3700a68bac5c9e78435ae4be4..dc37817a618931b021655c9da865c3521644455b 100644 (file)
@@ -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]
index 089b4d3d3c1c436b1268638ade43d87034e69b60..6a1a47e213a740e29f3f7ad815d929c923538646 100644 (file)
@@ -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()
index c91fe2845844f949de582018bf9d84e36114de7b..13dde21809c81d1deeba6c4de7df22c44aa9ddb0 100644 (file)
@@ -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 {
index a35ece13add7cdeb7e1cea722d06e0f0b03ff801..78e9dc26bc0d1b14545aa44d669fb665550e90d5 100644 (file)
@@ -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
        }