Skip to content

Commit caa4631

Browse files
committed
cmd/link: use TOC-relative trampolines on PPC64 when needed
When linking a PIE binary with the internal linker, TOC relative relocations need to be generated. Update trampolines to indirect call using R12 to more closely match the AIX/ELFv2 regardless of buildmode, and work with position-indepdent code. Likewise, update the check for offseting R_CALLPOWER relocs to make a local call. It should be checking ldr.AttrExternal, not ldr.IsExternal. This offset should not be adjusted for external (non-go) object files, it is handled when ELF reloc are translated into go relocs. And, update trampoline tests to verify these are generated correctly and produce a working binary using -buildmode=pie on ppc64le. Fixes #52337 Change-Id: I8a2dea06c3237bdf0e87888b56a17b6c4c99a7de Reviewed-on: https://go-review.googlesource.com/c/go/+/400234 Reviewed-by: Than McIntosh <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Run-TryBot: Paul Murphy <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent e8d9fd7 commit caa4631

File tree

2 files changed

+78
-71
lines changed

2 files changed

+78
-71
lines changed

src/cmd/link/internal/ppc64/asm.go

+23-28
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
766766
// For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in
767767
// r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created.
768768
if ctxt.IsExternal() && r2Valid(ctxt) {
769-
// No trampolines needed since r2 contains the TOC
769+
// The TOC pointer is valid. The external linker will insert trampolines.
770770
return
771771
}
772772

@@ -819,14 +819,9 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
819819
}
820820
}
821821
if ldr.SymType(tramp) == 0 {
822-
if r2Valid(ctxt) {
823-
// Should have returned for above cases
824-
ctxt.Errorf(s, "unexpected trampoline for shared or dynamic linking")
825-
} else {
826-
trampb := ldr.MakeSymbolUpdater(tramp)
827-
ctxt.AddTramp(trampb)
828-
gentramp(ctxt, ldr, trampb, rs, r.Add())
829-
}
822+
trampb := ldr.MakeSymbolUpdater(tramp)
823+
ctxt.AddTramp(trampb)
824+
gentramp(ctxt, ldr, trampb, rs, r.Add())
830825
}
831826
sb := ldr.MakeSymbolUpdater(s)
832827
relocs := sb.Relocs()
@@ -842,7 +837,6 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
842837
func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
843838
tramp.SetSize(16) // 4 instructions
844839
P := make([]byte, tramp.Size())
845-
t := ldr.SymValue(target) + offset
846840
var o1, o2 uint32
847841

848842
if ctxt.IsAIX() {
@@ -851,8 +845,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
851845
// However, all text symbols are accessed with a TOC symbol as
852846
// text relocations aren't supposed to be possible.
853847
// So, keep using the external linking way to be more AIX friendly.
854-
o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi
855-
o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo
848+
o1 = uint32(0x3c000000) | 12<<21 | 2<<16 // addis r12, r2, toctargetaddr hi
849+
o2 = uint32(0xe8000000) | 12<<21 | 12<<16 // ld r12, r12, toctargetaddr lo
856850

857851
toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0)
858852
toctramp.SetType(sym.SXCOFFTOC)
@@ -866,31 +860,32 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
866860
// Used for default build mode for an executable
867861
// Address of the call target is generated using
868862
// relocation and doesn't depend on r2 (TOC).
869-
o1 = uint32(0x3fe00000) // lis r31,targetaddr hi
870-
o2 = uint32(0x3bff0000) // addi r31,targetaddr lo
863+
o1 = uint32(0x3c000000) | 12<<21 // lis r12,targetaddr hi
864+
o2 = uint32(0x38000000) | 12<<21 | 12<<16 // addi r12,r12,targetaddr lo
871865

872-
// With external linking, the target address must be
873-
// relocated using LO and HA
874-
if ctxt.IsExternal() || ldr.SymValue(target) == 0 {
866+
t := ldr.SymValue(target)
867+
if t == 0 || r2Valid(ctxt) || ctxt.IsExternal() {
868+
// Target address is unknown, generate relocations
875869
r, _ := tramp.AddRel(objabi.R_ADDRPOWER)
870+
if r2Valid(ctxt) {
871+
// Use a TOC relative address if R2 holds the TOC pointer
872+
o1 |= uint32(2 << 16) // Transform lis r31,ha into addis r31,r2,ha
873+
r.SetType(objabi.R_ADDRPOWER_TOCREL)
874+
}
876875
r.SetOff(0)
877876
r.SetSiz(8) // generates 2 relocations: HA + LO
878877
r.SetSym(target)
879878
r.SetAdd(offset)
880879
} else {
881-
// adjustment needed if lo has sign bit set
882-
// when using addi to compute address
883-
val := uint32((t & 0xffff0000) >> 16)
884-
if t&0x8000 != 0 {
885-
val += 1
886-
}
887-
o1 |= val // hi part of addr
888-
o2 |= uint32(t & 0xffff) // lo part of addr
880+
// The target address is known, resolve it
881+
t += offset
882+
o1 |= (uint32(t) + 0x8000) >> 16 // HA
883+
o2 |= uint32(t) & 0xFFFF // LO
889884
}
890885
}
891886

892-
o3 := uint32(0x7fe903a6) // mtctr r31
893-
o4 := uint32(0x4e800420) // bctr
887+
o3 := uint32(0x7c0903a6) | 12<<21 // mtctr r12
888+
o4 := uint32(0x4e800420) // bctr
894889
ctxt.Arch.ByteOrder.PutUint32(P, o1)
895890
ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
896891
ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
@@ -962,7 +957,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
962957
// If we are linking PIE or shared code, all golang generated object files have an extra 2 instruction prologue
963958
// to regenerate the TOC pointer from R12. The exception are two special case functions tested below. Note,
964959
// local call offsets for externally generated objects are accounted for when converting into golang relocs.
965-
if !ldr.IsExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" {
960+
if !ldr.AttrExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" {
966961
// Furthermore, only apply the offset if the target looks like the start of a function call.
967962
if r.Add() == 0 && ldr.SymType(rs) == sym.STEXT {
968963
t += 8

src/cmd/link/link_test.go

+55-43
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,12 @@ func TestTrampoline(t *testing.T) {
642642
// For stress test, we set -debugtramp=2 flag, which sets a very low
643643
// threshold for trampoline generation, and essentially all cross-package
644644
// calls will use trampolines.
645+
buildmodes := []string{"default"}
645646
switch runtime.GOARCH {
646-
case "arm", "arm64", "ppc64", "ppc64le":
647+
case "arm", "arm64", "ppc64":
648+
case "ppc64le":
649+
// Trampolines are generated differently when internal linking PIE, test them too.
650+
buildmodes = append(buildmodes, "pie")
647651
default:
648652
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
649653
}
@@ -661,18 +665,20 @@ func TestTrampoline(t *testing.T) {
661665
}
662666
exe := filepath.Join(tmpdir, "hello.exe")
663667

664-
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
665-
out, err := cmd.CombinedOutput()
666-
if err != nil {
667-
t.Fatalf("build failed: %v\n%s", err, out)
668-
}
669-
cmd = exec.Command(exe)
670-
out, err = cmd.CombinedOutput()
671-
if err != nil {
672-
t.Errorf("executable failed to run: %v\n%s", err, out)
673-
}
674-
if string(out) != "hello\n" {
675-
t.Errorf("unexpected output:\n%s", out)
668+
for _, mode := range buildmodes {
669+
cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
670+
out, err := cmd.CombinedOutput()
671+
if err != nil {
672+
t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
673+
}
674+
cmd = exec.Command(exe)
675+
out, err = cmd.CombinedOutput()
676+
if err != nil {
677+
t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
678+
}
679+
if string(out) != "hello\n" {
680+
t.Errorf("unexpected output (%s):\n%s", mode, out)
681+
}
676682
}
677683
}
678684

@@ -693,8 +699,12 @@ func TestTrampolineCgo(t *testing.T) {
693699
// For stress test, we set -debugtramp=2 flag, which sets a very low
694700
// threshold for trampoline generation, and essentially all cross-package
695701
// calls will use trampolines.
702+
buildmodes := []string{"default"}
696703
switch runtime.GOARCH {
697-
case "arm", "arm64", "ppc64", "ppc64le":
704+
case "arm", "arm64", "ppc64":
705+
case "ppc64le":
706+
// Trampolines are generated differently when internal linking PIE, test them too.
707+
buildmodes = append(buildmodes, "pie")
698708
default:
699709
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
700710
}
@@ -713,37 +723,39 @@ func TestTrampolineCgo(t *testing.T) {
713723
}
714724
exe := filepath.Join(tmpdir, "hello.exe")
715725

716-
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
717-
out, err := cmd.CombinedOutput()
718-
if err != nil {
719-
t.Fatalf("build failed: %v\n%s", err, out)
720-
}
721-
cmd = exec.Command(exe)
722-
out, err = cmd.CombinedOutput()
723-
if err != nil {
724-
t.Errorf("executable failed to run: %v\n%s", err, out)
725-
}
726-
if string(out) != "hello\n" && string(out) != "hello\r\n" {
727-
t.Errorf("unexpected output:\n%s", out)
728-
}
726+
for _, mode := range buildmodes {
727+
cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
728+
out, err := cmd.CombinedOutput()
729+
if err != nil {
730+
t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
731+
}
732+
cmd = exec.Command(exe)
733+
out, err = cmd.CombinedOutput()
734+
if err != nil {
735+
t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
736+
}
737+
if string(out) != "hello\n" && string(out) != "hello\r\n" {
738+
t.Errorf("unexpected output (%s):\n%s", mode, out)
739+
}
729740

730-
// Test internal linking mode.
741+
// Test internal linking mode.
731742

732-
if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
733-
return // internal linking cgo is not supported
734-
}
735-
cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
736-
out, err = cmd.CombinedOutput()
737-
if err != nil {
738-
t.Fatalf("build failed: %v\n%s", err, out)
739-
}
740-
cmd = exec.Command(exe)
741-
out, err = cmd.CombinedOutput()
742-
if err != nil {
743-
t.Errorf("executable failed to run: %v\n%s", err, out)
744-
}
745-
if string(out) != "hello\n" && string(out) != "hello\r\n" {
746-
t.Errorf("unexpected output:\n%s", out)
743+
if runtime.GOARCH == "ppc64" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
744+
return // internal linking cgo is not supported
745+
}
746+
cmd = exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
747+
out, err = cmd.CombinedOutput()
748+
if err != nil {
749+
t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
750+
}
751+
cmd = exec.Command(exe)
752+
out, err = cmd.CombinedOutput()
753+
if err != nil {
754+
t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
755+
}
756+
if string(out) != "hello\n" && string(out) != "hello\r\n" {
757+
t.Errorf("unexpected output (%s):\n%s", mode, out)
758+
}
747759
}
748760
}
749761

0 commit comments

Comments
 (0)