aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/link/internal/ld/data.go38
-rw-r--r--src/cmd/link/internal/ppc64/asm.go73
2 files changed, 84 insertions, 27 deletions
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index aca8973a85..d007d2d180 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -324,18 +324,36 @@ func isRuntimeDepPkg(pkg string) bool {
return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test")
}
+// Estimate the max size needed to hold any new trampolines created for this function. This
+// is used to determine when the section can be split if it becomes too large, to ensure that
+// the trampolines are in the same section as the function that uses them.
+func maxSizeTrampolinesPPC64(s *Symbol, isTramp bool) uint64 {
+ // If Thearch.Trampoline is nil, then trampoline support is not available on this arch.
+ // A trampoline does not need any dependent trampolines.
+ if Thearch.Trampoline == nil || isTramp {
+ return 0
+ }
+
+ n := uint64(0)
+ for ri := range s.R {
+ r := &s.R[ri]
+ if r.Type.IsDirectJump() {
+ n++
+ }
+ }
+ // Trampolines in ppc64 are 4 instructions.
+ return n * 16
+}
+
// detect too-far jumps in function s, and add trampolines if necessary
-// ARM supports trampoline insertion for internal and external linking
-// PPC64 & PPC64LE support trampoline insertion for internal linking only
+// ARM, PPC64 & PPC64LE support trampoline insertion for internal and external linking
+// On PPC64 & PPC64LE the text sections might be split but will still insert trampolines
+// where necessary.
func trampoline(ctxt *Link, s *Symbol) {
if Thearch.Trampoline == nil {
return // no need or no support of trampolines on this arch
}
- if Linkmode == LinkExternal && SysArch.Family == sys.PPC64 {
- return
- }
-
for ri := range s.R {
r := &s.R[ri]
if !r.Type.IsDirectJump() {
@@ -2044,14 +2062,14 @@ func (ctxt *Link) textaddress() {
sect.Vaddr = va
ntramps := 0
for _, sym := range ctxt.Textp {
- sect, n, va = assignAddress(ctxt, sect, n, sym, va)
+ sect, n, va = assignAddress(ctxt, sect, n, sym, va, false)
trampoline(ctxt, sym) // resolve jumps, may add trampolines if jump too far
// lay down trampolines after each function
for ; ntramps < len(ctxt.tramps); ntramps++ {
tramp := ctxt.tramps[ntramps]
- sect, n, va = assignAddress(ctxt, sect, n, tramp, va)
+ sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true)
}
}
@@ -2077,7 +2095,7 @@ func (ctxt *Link) textaddress() {
// assigns address for a text symbol, returns (possibly new) section, its number, and the address
// Note: once we have trampoline insertion support for external linking, this function
// will not need to create new text sections, and so no need to return sect and n.
-func assignAddress(ctxt *Link, sect *Section, n int, sym *Symbol, va uint64) (*Section, int, uint64) {
+func assignAddress(ctxt *Link, sect *Section, n int, sym *Symbol, va uint64, isTramp bool) (*Section, int, uint64) {
sym.Sect = sect
if sym.Type&obj.SSUB != 0 {
return sect, n, va
@@ -2106,7 +2124,7 @@ func assignAddress(ctxt *Link, sect *Section, n int, sym *Symbol, va uint64) (*S
// Only break at outermost syms.
- if SysArch.InFamily(sys.PPC64) && sym.Outer == nil && Iself && Linkmode == LinkExternal && va-sect.Vaddr+funcsize > 0x1c00000 {
+ if SysArch.InFamily(sys.PPC64) && sym.Outer == nil && Iself && Linkmode == LinkExternal && va-sect.Vaddr+funcsize+maxSizeTrampolinesPPC64(sym, isTramp) > 0x1c00000 {
// Set the length for the previous text section
sect.Length = va - sect.Vaddr
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index cf2c532f9e..9f2925fc51 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -522,13 +522,22 @@ func archrelocaddr(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
// resolve direct jump relocation r in s, and add trampoline if necessary
func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
+ // Trampolines are created if the branch offset is too large and the linker cannot insert a call stub to handle it.
+ // For internal linking, trampolines are always created for long calls.
+ // 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 ld.Linkmode == ld.LinkExternal && (ctxt.DynlinkingGo() || ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE) {
+ // No trampolines needed since r2 contains the TOC
+ return
+ }
+
t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off))
switch r.Type {
case obj.R_CALLPOWER:
// If branch offset is too far then create a trampoline.
- if int64(int32(t<<6)>>6) != t || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) {
+ if (ld.Linkmode == ld.LinkExternal && s.Sect != r.Sym.Sect) || (ld.Linkmode == ld.LinkInternal && int64(int32(t<<6)>>6) != t) || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) {
var tramp *ld.Symbol
for i := 0; ; i++ {
@@ -552,26 +561,20 @@ func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
t = ld.Symaddr(tramp) + r.Add - (s.Value + int64(r.Off))
- // If the offset of the trampoline that has been found is within range, use it.
- if int64(int32(t<<6)>>6) == t {
+ // With internal linking, the trampoline can be used if it is not too far.
+ // With external linking, the trampoline must be in this section for it to be reused.
+ if (ld.Linkmode == ld.LinkInternal && int64(int32(t<<6)>>6) == t) || (ld.Linkmode == ld.LinkExternal && s.Sect == tramp.Sect) {
break
}
}
if tramp.Type == 0 {
- ctxt.AddTramp(tramp)
- tramp.Size = 16 // 4 instructions
- tramp.P = make([]byte, tramp.Size)
- t = ld.Symaddr(r.Sym) + r.Add
- f := t & 0xffff0000
- o1 := uint32(0x3fe00000 | (f >> 16)) // lis r31,trampaddr hi (r31 is temp reg)
- f = t & 0xffff
- o2 := uint32(0x63ff0000 | f) // ori r31,trampaddr lo
- o3 := uint32(0x7fe903a6) // mtctr
- o4 := uint32(0x4e800420) // bctr
- ld.SysArch.ByteOrder.PutUint32(tramp.P, o1)
- ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2)
- ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3)
- ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4)
+ if ctxt.DynlinkingGo() || ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE {
+ // Should have returned for above cases
+ ld.Errorf(s, "unexpected trampoline for shared or dynamic linking\n")
+ } else {
+ ctxt.AddTramp(tramp)
+ gentramp(tramp, r.Sym, int64(r.Add))
+ }
}
r.Sym = tramp
r.Add = 0 // This was folded into the trampoline target address
@@ -582,6 +585,42 @@ func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
}
}
+func gentramp(tramp, target *ld.Symbol, offset int64) {
+ // Used for default build mode for an executable
+ // Address of the call target is generated using
+ // relocation and doesn't depend on r2 (TOC).
+ tramp.Size = 16 // 4 instructions
+ tramp.P = make([]byte, tramp.Size)
+ t := ld.Symaddr(target) + offset
+ o1 := uint32(0x3fe00000) // lis r31,targetaddr hi
+ o2 := uint32(0x3bff0000) // addi r31,targetaddr lo
+ // With external linking, the target address must be
+ // relocated using LO and HA
+ if ld.Linkmode == ld.LinkExternal {
+ tr := ld.Addrel(tramp)
+ tr.Off = 0
+ tr.Type = obj.R_ADDRPOWER
+ tr.Siz = 8 // generates 2 relocations: HA + LO
+ tr.Sym = target
+ tr.Add = 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
+ }
+ o3 := uint32(0x7fe903a6) // mtctr r31
+ o4 := uint32(0x4e800420) // bctr
+ ld.SysArch.ByteOrder.PutUint32(tramp.P, o1)
+ ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2)
+ ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3)
+ ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4)
+}
+
func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
if ld.Linkmode == ld.LinkExternal {
switch r.Type {