Skip to content

Commit

Permalink
cmd/link: add trampolines for too far calls in ppc64x
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Cherry Zhang <[email protected]>
  • Loading branch information
laboger authored and cherrymui committed Oct 17, 2016
1 parent 0ce1d79 commit d26b066
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 8 deletions.
73 changes: 68 additions & 5 deletions src/cmd/link/internal/ppc64/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/cmd/link/internal/ppc64/obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 19 additions & 3 deletions src/cmd/link/linkbig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit d26b066

Please sign in to comment.