// 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
}
}
}
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()
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() {
// 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)
// 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)
// 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
// 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)
}
}
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)
+ }
}
}
// 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)
}
}
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)
+ }
}
}