aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-05-09 14:34:16 -0700
committerIan Lance Taylor <iant@golang.org>2017-05-09 23:36:51 +0000
commit5331e7e9df017374a05a66497fd367e165b8aaf5 (patch)
tree4db5424e6ccafca41f1e200ac8289e594f40564e
parent08dca4c649ba7f90f209376ba342bd46ef14ebe5 (diff)
downloadgo-5331e7e9df017374a05a66497fd367e165b8aaf5.tar.xz
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 <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>
-rw-r--r--misc/cgo/testcarchive/carchive_test.go134
-rw-r--r--src/cmd/internal/goobj/read.go2
-rw-r--r--src/cmd/internal/obj/objfile.go3
-rw-r--r--src/cmd/internal/obj/ppc64/obj9.go5
-rw-r--r--src/cmd/internal/objabi/doc.go1
-rw-r--r--src/cmd/link/internal/ld/link.go4
-rw-r--r--src/cmd/link/internal/ld/objfile.go3
-rw-r--r--src/cmd/link/internal/ld/symtab.go5
8 files changed, 101 insertions, 56 deletions
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
}