diff options
author | Paul E. Murphy <murp@ibm.com> | 2021-05-03 11:00:54 -0500 |
---|---|---|
committer | Heschi Kreinick <heschi@google.com> | 2022-07-06 20:55:56 +0000 |
commit | 3a7cec27738c51347f4e5a466410a0b2089391b7 (patch) | |
tree | 163cb518c6ec7ad7f6b4aef4da573b98b39a9163 | |
parent | b80ae102217ad4fefb7e72e060500f42d252a50a (diff) | |
download | go-3a7cec27738c51347f4e5a466410a0b2089391b7.tar.gz go-3a7cec27738c51347f4e5a466410a0b2089391b7.zip |
[release-branch.go1.18] 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 #53107
Change-Id: I8a2dea06c3237bdf0e87888b56a17b6c4c99a7de
Reviewed-on: https://go-review.googlesource.com/c/go/+/400234
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Paul Murphy <murp@ibm.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-on: https://go-review.googlesource.com/c/go/+/409075
Run-TryBot: Cherry Mui <cherryyz@google.com>
-rw-r--r-- | src/cmd/link/internal/ppc64/asm.go | 51 | ||||
-rw-r--r-- | src/cmd/link/link_test.go | 98 |
2 files changed, 78 insertions, 71 deletions
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index d2b140b45d..73c2718a33 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -766,7 +766,7 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { // For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in // r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created. if ctxt.IsExternal() && r2Valid(ctxt) { - // No trampolines needed since r2 contains the TOC + // The TOC pointer is valid. The external linker will insert trampolines. return } @@ -819,14 +819,9 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { } } if ldr.SymType(tramp) == 0 { - if r2Valid(ctxt) { - // Should have returned for above cases - ctxt.Errorf(s, "unexpected trampoline for shared or dynamic linking") - } else { - trampb := ldr.MakeSymbolUpdater(tramp) - ctxt.AddTramp(trampb) - gentramp(ctxt, ldr, trampb, rs, r.Add()) - } + trampb := ldr.MakeSymbolUpdater(tramp) + ctxt.AddTramp(trampb) + gentramp(ctxt, ldr, trampb, rs, r.Add()) } sb := ldr.MakeSymbolUpdater(s) relocs := sb.Relocs() @@ -842,7 +837,6 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { tramp.SetSize(16) // 4 instructions P := make([]byte, tramp.Size()) - t := ldr.SymValue(target) + offset var o1, o2 uint32 if ctxt.IsAIX() { @@ -851,8 +845,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta // However, all text symbols are accessed with a TOC symbol as // text relocations aren't supposed to be possible. // So, keep using the external linking way to be more AIX friendly. - o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi - o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo + o1 = uint32(0x3c000000) | 12<<21 | 2<<16 // addis r12, r2, toctargetaddr hi + o2 = uint32(0xe8000000) | 12<<21 | 12<<16 // ld r12, r12, toctargetaddr lo toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0) toctramp.SetType(sym.SXCOFFTOC) @@ -866,31 +860,32 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta // Used for default build mode for an executable // Address of the call target is generated using // relocation and doesn't depend on r2 (TOC). - o1 = uint32(0x3fe00000) // lis r31,targetaddr hi - o2 = uint32(0x3bff0000) // addi r31,targetaddr lo + o1 = uint32(0x3c000000) | 12<<21 // lis r12,targetaddr hi + o2 = uint32(0x38000000) | 12<<21 | 12<<16 // addi r12,r12,targetaddr lo - // With external linking, the target address must be - // relocated using LO and HA - if ctxt.IsExternal() || ldr.SymValue(target) == 0 { + t := ldr.SymValue(target) + if t == 0 || r2Valid(ctxt) || ctxt.IsExternal() { + // Target address is unknown, generate relocations r, _ := tramp.AddRel(objabi.R_ADDRPOWER) + if r2Valid(ctxt) { + // Use a TOC relative address if R2 holds the TOC pointer + o1 |= uint32(2 << 16) // Transform lis r31,ha into addis r31,r2,ha + r.SetType(objabi.R_ADDRPOWER_TOCREL) + } r.SetOff(0) r.SetSiz(8) // generates 2 relocations: HA + LO r.SetSym(target) r.SetAdd(offset) } else { - // adjustment needed if lo has sign bit set - // when using addi to compute address - val := uint32((t & 0xffff0000) >> 16) - if t&0x8000 != 0 { - val += 1 - } - o1 |= val // hi part of addr - o2 |= uint32(t & 0xffff) // lo part of addr + // The target address is known, resolve it + t += offset + o1 |= (uint32(t) + 0x8000) >> 16 // HA + o2 |= uint32(t) & 0xFFFF // LO } } - o3 := uint32(0x7fe903a6) // mtctr r31 - o4 := uint32(0x4e800420) // bctr + o3 := uint32(0x7c0903a6) | 12<<21 // mtctr r12 + o4 := uint32(0x4e800420) // bctr ctxt.Arch.ByteOrder.PutUint32(P, o1) ctxt.Arch.ByteOrder.PutUint32(P[4:], o2) ctxt.Arch.ByteOrder.PutUint32(P[8:], o3) @@ -962,7 +957,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade // If we are linking PIE or shared code, all golang generated object files have an extra 2 instruction prologue // to regenerate the TOC pointer from R12. The exception are two special case functions tested below. Note, // local call offsets for externally generated objects are accounted for when converting into golang relocs. - if !ldr.IsExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" { + if !ldr.AttrExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" { // Furthermore, only apply the offset if the target looks like the start of a function call. if r.Add() == 0 && ldr.SymType(rs) == sym.STEXT { t += 8 diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index ad7658bb25..d58ee8669b 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -642,8 +642,12 @@ func TestTrampoline(t *testing.T) { // For stress test, we set -debugtramp=2 flag, which sets a very low // threshold for trampoline generation, and essentially all cross-package // calls will use trampolines. + buildmodes := []string{"default"} switch runtime.GOARCH { - case "arm", "arm64", "ppc64", "ppc64le": + case "arm", "arm64", "ppc64": + case "ppc64le": + // Trampolines are generated differently when internal linking PIE, test them too. + buildmodes = append(buildmodes, "pie") default: t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) } @@ -661,18 +665,20 @@ func TestTrampoline(t *testing.T) { } exe := filepath.Join(tmpdir, "hello.exe") - cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src) - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("build failed: %v\n%s", err, out) - } - cmd = exec.Command(exe) - out, err = cmd.CombinedOutput() - if err != nil { - t.Errorf("executable failed to run: %v\n%s", err, out) - } - if string(out) != "hello\n" { - t.Errorf("unexpected output:\n%s", out) + for _, mode := range buildmodes { + cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) + } + cmd = exec.Command(exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) + } + if string(out) != "hello\n" { + t.Errorf("unexpected output (%s):\n%s", mode, out) + } } } @@ -693,8 +699,12 @@ func TestTrampolineCgo(t *testing.T) { // For stress test, we set -debugtramp=2 flag, which sets a very low // threshold for trampoline generation, and essentially all cross-package // calls will use trampolines. + buildmodes := []string{"default"} switch runtime.GOARCH { - case "arm", "arm64", "ppc64", "ppc64le": + case "arm", "arm64", "ppc64": + case "ppc64le": + // Trampolines are generated differently when internal linking PIE, test them too. + buildmodes = append(buildmodes, "pie") default: t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) } @@ -713,37 +723,39 @@ func TestTrampolineCgo(t *testing.T) { } exe := filepath.Join(tmpdir, "hello.exe") - cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src) - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("build failed: %v\n%s", err, out) - } - cmd = exec.Command(exe) - out, err = cmd.CombinedOutput() - if err != nil { - t.Errorf("executable failed to run: %v\n%s", err, out) - } - if string(out) != "hello\n" && string(out) != "hello\r\n" { - t.Errorf("unexpected output:\n%s", out) - } + for _, mode := range buildmodes { + cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) + } + cmd = exec.Command(exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) + } + if string(out) != "hello\n" && string(out) != "hello\r\n" { + t.Errorf("unexpected output (%s):\n%s", mode, out) + } - // Test internal linking mode. + // Test internal linking mode. - if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() { - return // internal linking cgo is not supported - } - cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src) - out, err = cmd.CombinedOutput() - if err != nil { - t.Fatalf("build failed: %v\n%s", err, out) - } - cmd = exec.Command(exe) - out, err = cmd.CombinedOutput() - if err != nil { - t.Errorf("executable failed to run: %v\n%s", err, out) - } - if string(out) != "hello\n" && string(out) != "hello\r\n" { - t.Errorf("unexpected output:\n%s", out) + if runtime.GOARCH == "ppc64" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() { + return // internal linking cgo is not supported + } + cmd = exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) + } + cmd = exec.Command(exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) + } + if string(out) != "hello\n" && string(out) != "hello\r\n" { + t.Errorf("unexpected output (%s):\n%s", mode, out) + } } } |