cmd/link: support trampoline insertion for PLT calls on ARM64

When internal linking with C objects, some C object relocations
may be turned into a CALL via PLT. For very large programs, the
PLT stub may be laid too far.

PLT stubs are generated late in the linker, and laid out after
the end of the text section. So if the text section is big, the
PLT stubs are likely too far.

To avoid this situation, add trampolines for PLT calls in the
trampoline pass. Only do this when the program is known too large
(i.e. the second pass of the two-pass algorithm).

Updates #40492.

Change-Id: I21f65d6cbc6bde84e3cf9c2ae05f5233df6cfa72
Reviewed-on: https://go-review.googlesource.com/c/go/+/314452
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Cherry Zhang 2021-04-27 10:57:09 -04:00
parent f12dfeac89
commit 657f58d845
2 changed files with 32 additions and 4 deletions

View file

@ -441,6 +441,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
r2.SetOff(r.Off() + 4)
r2.SetSym(syms.GOT)
r2.SetAdd(int64(ldr.SymGot(targ)))
return true
}
return false
}
@ -1187,7 +1188,14 @@ func offsetLabelName(ldr *loader.Loader, s loader.Sym, off int64) string {
func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
relocs := ldr.Relocs(s)
r := relocs.At(ri)
const pcrel = 1
switch r.Type() {
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26),
objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
// Host object relocations that will be turned into a PLT call.
// The PLT may be too far. Insert a trampoline for them.
fallthrough
case objabi.R_CALLARM64:
var t int64
// ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
@ -1196,7 +1204,7 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
if ldr.SymValue(rs) != 0 {
t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
}
if t >= 1<<27 || t < -1<<27 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) {
if t >= 1<<27 || t < -1<<27 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && (ldr.SymPkg(s) == "" || ldr.SymPkg(s) != ldr.SymPkg(rs))) {
// direct call too far, need to insert trampoline.
// look up existing trampolines first. if we found one within the range
// of direct call, we can reuse it. otherwise create a new one.

View file

@ -39,6 +39,7 @@ import (
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"compress/zlib"
"debug/elf"
"encoding/binary"
"fmt"
"log"
@ -98,7 +99,8 @@ func trampoline(ctxt *Link, s loader.Sym) {
relocs := ldr.Relocs(s)
for ri := 0; ri < relocs.Count(); ri++ {
r := relocs.At(ri)
if !r.Type().IsDirectCallOrJump() {
rt := r.Type()
if !rt.IsDirectCallOrJump() && !isPLTCall(rt) {
continue
}
rs := r.Sym()
@ -107,8 +109,11 @@ func trampoline(ctxt *Link, s loader.Sym) {
}
rs = ldr.ResolveABIAlias(rs)
if ldr.SymValue(rs) == 0 && (ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT) {
if ldr.SymPkg(rs) == ldr.SymPkg(s) {
continue // symbols in the same package are laid out together
if ldr.SymPkg(s) != "" && ldr.SymPkg(rs) == ldr.SymPkg(s) {
// Symbols in the same package are laid out together.
// Except that if SymPkg(s) == "", it is a host object symbol
// which may call an external symbol via PLT.
continue
}
if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) {
continue // runtime packages are laid out together
@ -119,6 +124,21 @@ func trampoline(ctxt *Link, s loader.Sym) {
}
}
// whether rt is a (host object) relocation that will be turned into
// a call to PLT.
func isPLTCall(rt objabi.RelocType) bool {
const pcrel = 1
switch rt {
// ARM64
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26),
objabi.MachoRelocOffset + MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
return true
}
// TODO: other architectures.
return false
}
// FoldSubSymbolOffset computes the offset of symbol s to its top-level outer
// symbol. Returns the top-level symbol and the offset.
// This is used in generating external relocations.