Skip to content

Commit 5331e7e

Browse files
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Michael Hudson-Doyle <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent 08dca4c commit 5331e7e

File tree

8 files changed

+101
-56
lines changed

8 files changed

+101
-56
lines changed

misc/cgo/testcarchive/carchive_test.go

Lines changed: 80 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,7 @@ func TestEarlySignalHandler(t *testing.T) {
218218
}
219219

220220
func TestSignalForwarding(t *testing.T) {
221-
switch GOOS {
222-
case "darwin":
223-
switch GOARCH {
224-
case "arm", "arm64":
225-
t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
226-
}
227-
case "windows":
228-
t.Skip("skipping signal test on Windows")
229-
}
221+
checkSignalForwardingTest(t)
230222

231223
defer func() {
232224
os.Remove("libgo2.a")
@@ -251,51 +243,19 @@ func TestSignalForwarding(t *testing.T) {
251243
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
252244

253245
out, err := cmd.CombinedOutput()
254-
255-
if err == nil {
256-
t.Logf("%s", out)
257-
t.Error("test program succeeded unexpectedly")
258-
} else if ee, ok := err.(*exec.ExitError); !ok {
259-
t.Logf("%s", out)
260-
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
261-
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
262-
t.Logf("%s", out)
263-
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
264-
} else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV {
265-
t.Logf("%s", out)
266-
t.Errorf("got %v; expected SIGSEGV", ee)
267-
}
246+
t.Logf("%s", out)
247+
expectSignal(t, err, syscall.SIGSEGV)
268248

269249
// Test SIGPIPE forwarding
270250
cmd = exec.Command(bin[0], append(bin[1:], "3")...)
271251

272252
out, err = cmd.CombinedOutput()
273-
274-
if err == nil {
275-
t.Logf("%s", out)
276-
t.Error("test program succeeded unexpectedly")
277-
} else if ee, ok := err.(*exec.ExitError); !ok {
278-
t.Logf("%s", out)
279-
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
280-
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
281-
t.Logf("%s", out)
282-
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
283-
} else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE {
284-
t.Logf("%s", out)
285-
t.Errorf("got %v; expected SIGPIPE", ee)
286-
}
253+
t.Logf("%s", out)
254+
expectSignal(t, err, syscall.SIGPIPE)
287255
}
288256

289257
func TestSignalForwardingExternal(t *testing.T) {
290-
switch GOOS {
291-
case "darwin":
292-
switch GOARCH {
293-
case "arm", "arm64":
294-
t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
295-
}
296-
case "windows":
297-
t.Skip("skipping signal test on Windows")
298-
}
258+
checkSignalForwardingTest(t)
299259

300260
defer func() {
301261
os.Remove("libgo2.a")
@@ -363,21 +323,46 @@ func TestSignalForwardingExternal(t *testing.T) {
363323
continue
364324
}
365325

366-
if ee, ok := err.(*exec.ExitError); !ok {
367-
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
368-
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
369-
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
370-
} else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV {
371-
t.Errorf("got %v; expected SIGSEGV", ee)
372-
} else {
373-
// We got the error we expected.
326+
if expectSignal(t, err, syscall.SIGSEGV) {
374327
return
375328
}
376329
}
377330

378331
t.Errorf("program succeeded unexpectedly %d times", tries)
379332
}
380333

334+
// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
335+
// doesn't work on this platform.
336+
func checkSignalForwardingTest(t *testing.T) {
337+
switch GOOS {
338+
case "darwin":
339+
switch GOARCH {
340+
case "arm", "arm64":
341+
t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
342+
}
343+
case "windows":
344+
t.Skip("skipping signal test on Windows")
345+
}
346+
}
347+
348+
// expectSignal checks that err, the exit status of a test program,
349+
// shows a failure due to a specific signal. Returns whether we found
350+
// the expected signal.
351+
func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
352+
if err == nil {
353+
t.Error("test program succeeded unexpectedly")
354+
} else if ee, ok := err.(*exec.ExitError); !ok {
355+
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
356+
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
357+
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
358+
} else if !ws.Signaled() || ws.Signal() != sig {
359+
t.Errorf("got %v; expected signal %v", ee, sig)
360+
} else {
361+
return true
362+
}
363+
return false
364+
}
365+
381366
func TestOsSignal(t *testing.T) {
382367
switch GOOS {
383368
case "windows":
@@ -592,3 +577,44 @@ func TestSIGPROF(t *testing.T) {
592577
t.Fatal(err)
593578
}
594579
}
580+
581+
// TestCompileWithoutShared tests that if we compile code without the
582+
// -shared option, we can put it into an archive. When we use the go
583+
// tool with -buildmode=c-archive, it passes -shared to the compiler,
584+
// so we override that. The go tool doesn't work this way, but Bazel
585+
// will likely do it in the future. And it ought to work. This test
586+
// was added because at one time it did not work on PPC GNU/Linux.
587+
func TestCompileWithoutShared(t *testing.T) {
588+
// For simplicity, reuse the signal forwarding test.
589+
checkSignalForwardingTest(t)
590+
591+
defer func() {
592+
os.Remove("libgo2.a")
593+
os.Remove("libgo2.h")
594+
}()
595+
596+
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "libgo2")
597+
cmd.Env = gopathEnv
598+
t.Log(cmd.Args)
599+
out, err := cmd.CombinedOutput()
600+
t.Logf("%s", out)
601+
if err != nil {
602+
t.Fatal(err)
603+
}
604+
605+
exe := "./testnoshared" + exeSuffix
606+
ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a")
607+
t.Log(ccArgs)
608+
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
609+
t.Logf("%s", out)
610+
if err != nil {
611+
t.Fatal(err)
612+
}
613+
defer os.Remove(exe)
614+
615+
binArgs := append(cmdToRun(exe), "3")
616+
t.Log(binArgs)
617+
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
618+
t.Logf("%s", out)
619+
expectSignal(t, err, syscall.SIGPIPE)
620+
}

src/cmd/internal/goobj/read.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ func (r *objReader) parseObject(prefix []byte) error {
532532
f.Args = r.readInt()
533533
f.Frame = r.readInt()
534534
flags := r.readInt()
535-
f.Leaf = flags&1 != 0
535+
f.Leaf = flags&(1<<0) != 0
536536
f.NoSplit = r.readInt() != 0
537537
f.Var = make([]Var, r.readInt())
538538
for i := range f.Var {

src/cmd/internal/obj/objfile.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@ func (w *objWriter) writeSym(s *LSym) {
338338
if s.ReflectMethod() {
339339
flags |= 1 << 2
340340
}
341+
if ctxt.Flag_shared {
342+
flags |= 1 << 3
343+
}
341344
w.writeInt(flags)
342345
w.writeInt(int64(len(s.Func.Autom)))
343346
for _, a := range s.Func.Autom {

src/cmd/internal/obj/ppc64/obj9.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
476476
// generate the addis instruction except as part of the
477477
// load of a large constant, and in that case there is no
478478
// way to use r12 as the source.
479+
//
480+
// Note that the same condition is tested in
481+
// putelfsym in cmd/link/internal/ld/symtab.go
482+
// where we set the st_other field to indicate
483+
// the presence of these instructions.
479484
q = obj.Appendp(q, c.newprog)
480485
q.As = AWORD
481486
q.Pos = p.Pos

src/cmd/internal/objabi/doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
// 1<<0 leaf
7878
// 1<<1 C function
7979
// 1<<2 function may call reflect.Type.Method
80+
// 1<<3 function compiled with -shared
8081
// - nlocal [int]
8182
// - local [nlocal automatics]
8283
// - pcln [pcln table]

src/cmd/link/internal/ld/link.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ const (
135135
// AttrMakeTypelink Amarks types that should be added to the typelink
136136
// table. See typelinks.go:typelinks().
137137
AttrMakeTypelink
138+
// AttrShared marks symbols compiled with the -shared option.
139+
AttrShared
140+
// 14 attributes defined so far.
138141
)
139142

140143
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
@@ -150,6 +153,7 @@ func (a Attribute) OnList() bool { return a&AttrOnList != 0 }
150153
func (a Attribute) Local() bool { return a&AttrLocal != 0 }
151154
func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 }
152155
func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 }
156+
func (a Attribute) Shared() bool { return a&AttrShared != 0 }
153157

154158
func (a Attribute) CgoExport() bool {
155159
return a.CgoExportDynamic() || a.CgoExportStatic()

src/cmd/link/internal/ld/objfile.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ overwrite:
258258
if flags&(1<<2) != 0 {
259259
s.Attr |= AttrReflectMethod
260260
}
261+
if flags&(1<<3) != 0 {
262+
s.Attr |= AttrShared
263+
}
261264
n := r.readInt()
262265
pc.Autom = r.autom[:n:n]
263266
if !isdup {

src/cmd/link/internal/ld/symtab.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,13 @@ func putelfsym(ctxt *Link, x *Symbol, s string, t SymbolType, addr int64, go_ *S
147147
if x.Type&SHIDDEN != 0 {
148148
other = STV_HIDDEN
149149
}
150-
if (Buildmode == BuildmodeCArchive || Buildmode == BuildmodePIE || ctxt.DynlinkingGo()) && SysArch.Family == sys.PPC64 && typ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
150+
if SysArch.Family == sys.PPC64 && typ == STT_FUNC && x.Attr.Shared() && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
151151
// On ppc64 the top three bits of the st_other field indicate how
152152
// many instructions separate the global and local entry points. In
153153
// our case it is two instructions, indicated by the value 3.
154+
// The conditions here match those in preprocess in
155+
// cmd/internal/obj/ppc64/obj9.go, which is where the
156+
// instructions are inserted.
154157
other |= 3 << 5
155158
}
156159

0 commit comments

Comments
 (0)