]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: add trampolines for too far calls in ppc64x
authorLynn Boger <laboger@linux.vnet.ibm.com>
Tue, 11 Oct 2016 14:26:40 +0000 (09:26 -0500)
committerCherry Zhang <cherryyz@google.com>
Mon, 17 Oct 2016 16:06:04 +0000 (16:06 +0000)
This change adds support for trampolines on ppc64x when using
internal linking, in the case where the offset to the branch
target is larger than what fits in the field provided by the
branch instruction.

Fixes #16665

Change-Id: Icfee72910f38c94588d2adce517b64dee6176145
Reviewed-on: https://go-review.googlesource.com/30850
Reviewed-by: David Crawshaw <crawshaw@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
src/cmd/link/internal/ppc64/asm.go
src/cmd/link/internal/ppc64/obj.go
src/cmd/link/linkbig_test.go

index 7d9094ba470efe916573368147472a022548be8e..97107b9e5235629fbaeb40f024420f7d5a8e3215 100644 (file)
@@ -176,8 +176,8 @@ func genaddmoduledata(ctxt *ld.Link) {
        // blr
        o(0x4e800020)
 
-       ctxt.Textp = append(ctxt.Textp, initfunc)
        initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       ctxt.Textp = append(ctxt.Textp, initfunc)
        initarray_entry.Attr |= ld.AttrReachable
        initarray_entry.Attr |= ld.AttrLocal
        initarray_entry.Type = obj.SINITARR
@@ -513,6 +513,69 @@ func archrelocaddr(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
        return 0
 }
 
+// resolve direct jump relocation r in s, and add trampoline if necessary
+func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
+
+       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) {
+                       var tramp *ld.Symbol
+                       for i := 0; ; i++ {
+
+                               // Using r.Add as part of the name is significant in functions like duffzero where the call
+                               // target is at some offset within the function.  Calls to duff+8 and duff+256 must appear as
+                               // distinct trampolines.
+
+                               name := r.Sym.Name
+                               if r.Add == 0 {
+                                       name = name + fmt.Sprintf("-tramp%d", i)
+                               } else {
+                                       name = name + fmt.Sprintf("%+x-tramp%d", r.Add, i)
+                               }
+
+                               // Look up the trampoline in case it already exists
+
+                               tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+                               if tramp.Value == 0 {
+                                       break
+                               }
+
+                               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 {
+                                       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)
+                       }
+                       r.Sym = tramp
+                       r.Add = 0 // This was folded into the trampoline target address
+                       r.Done = 0
+               }
+       default:
+               ld.Errorf(s, "trampoline called with non-jump reloc: %v", r.Type)
+       }
+}
+
 func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
        if ld.Linkmode == ld.LinkExternal {
                switch r.Type {
@@ -573,15 +636,15 @@ func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
                // Bits 6 through 29 = (S + A - P) >> 2
 
                t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off))
+
                if t&3 != 0 {
                        ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
                }
+               // If branch offset is too far then create a trampoline.
+
                if int64(int32(t<<6)>>6) != t {
-                       // TODO(austin) This can happen if text > 32M.
-                       // Add a call trampoline to .text in that case.
-                       ld.Errorf(s, "relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t)
+                       ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
                }
-
                *val |= int64(uint32(t) &^ 0xfc000003)
                return 0
 
index bfff361328ab346dcfe3c2fe4c663975b8f06fbd..92cb1e8ebe54ce0994c07d9e6ea5322283661a26 100644 (file)
@@ -58,6 +58,7 @@ func Init() {
        ld.Thearch.Elfreloc1 = elfreloc1
        ld.Thearch.Elfsetupplt = elfsetupplt
        ld.Thearch.Gentext = gentext
+       ld.Thearch.Trampoline = trampoline
        ld.Thearch.Machoreloc1 = machoreloc1
        if ld.SysArch == sys.ArchPPC64LE {
                ld.Thearch.Lput = ld.Lputl
index b4fa5c747b06612f3baceb4f70d8193f5f7baecb..ce251704aa95fba3778aa2360909e67bad04378b 100644 (file)
@@ -77,15 +77,31 @@ func TestLargeText(t *testing.T) {
                t.Fatalf("can't write output: %v\n", err)
        }
 
+       // Build and run with internal linking.
+
        os.Chdir(tmpdir)
-       cmd := exec.Command("go", "build", "-o", "bigtext", "-ldflags", "'-linkmode=external'")
+       cmd := exec.Command("go", "build", "-o", "bigtext")
        out, err := cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("Build of big text program failed: %v, output: %s", err, out)
+               t.Fatalf("Build failed for big text program with internal linking: %v, output: %s", err, out)
+       }
+       cmd = exec.Command(tmpdir + "/bigtext")
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("Program built with internal linking failed to run with err %v, output: %s", err, out)
+       }
+
+       // Build and run with external linking
+
+       os.Chdir(tmpdir)
+       cmd = exec.Command("go", "build", "-o", "bigtext", "-ldflags", "'-linkmode=external'")
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("Build failed for big text program with external linking: %v, output: %s", err, out)
        }
        cmd = exec.Command(tmpdir + "/bigtext")
        out, err = cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("Program failed with err %v, output: %s", err, out)
+               t.Fatalf("Program built with external linking failed to run with err %v, output: %s", err, out)
        }
 }