mirror of
https://github.com/golang/go
synced 2024-09-29 01:14:29 -06:00
cmd/link/internal/ppc64: support non-PIC PLT call stubs
Simplify the PLT stub generation code to minimize stub generation knowing there is only ever a single TOC pointer when linking internally. The OpenBSD port requires Go make dynamic calls into its C library, so the linker must create stubs which work without R2 being set up. This new case is exactly case 3 described in the PPC64 ELFv2 1.5 section 4.2.5.3. Updates #56001 Change-Id: I07ebd08442302e55b94b57db474dfd7e7a0c2ac9 Reviewed-on: https://go-review.googlesource.com/c/go/+/488316 Auto-Submit: Carlos Amedee <carlos@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com> Run-TryBot: Paul Murphy <murp@ibm.com> Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com> Reviewed-by: Carlos Amedee <carlos@golang.org>
This commit is contained in:
parent
767fbe01ae
commit
f742ddc349
@ -48,17 +48,18 @@ import (
|
||||
// The build configuration supports PC-relative instructions and relocations (limited to tested targets).
|
||||
var hasPCrel = buildcfg.GOPPC64 >= 10 && buildcfg.GOOS == "linux"
|
||||
|
||||
func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (sym loader.Sym, firstUse bool) {
|
||||
func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, ri int, s loader.Sym) (sym loader.Sym, firstUse bool) {
|
||||
// The ppc64 ABI PLT has similar concepts to other
|
||||
// architectures, but is laid out quite differently. When we
|
||||
// see an R_PPC64_REL24 relocation to a dynamic symbol
|
||||
// (indicating that the call needs to go through the PLT), we
|
||||
// generate up to three stubs and reserve a PLT slot.
|
||||
// see a relocation to a dynamic symbol (indicating that the
|
||||
// call needs to go through the PLT), we generate up to three
|
||||
// stubs and reserve a PLT slot.
|
||||
//
|
||||
// 1) The call site will be bl x; nop (where the relocation
|
||||
// applies to the bl). We rewrite this to bl x_stub; ld
|
||||
// r2,24(r1). The ld is necessary because x_stub will save
|
||||
// r2 (the TOC pointer) at 24(r1) (the "TOC save slot").
|
||||
// 1) The call site is a "bl x" where genpltstub rewrites it to
|
||||
// "bl x_stub". Depending on the properties of the caller
|
||||
// (see ELFv2 1.5 4.2.5.3), a nop may be expected immediately
|
||||
// after the bl. This nop is rewritten to ld r2,24(r1) to
|
||||
// restore the toc pointer saved by x_stub.
|
||||
//
|
||||
// 2) We reserve space for a pointer in the .plt section (once
|
||||
// per referenced dynamic function). .plt is a data
|
||||
@ -67,11 +68,8 @@ func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, s loader.Sym)
|
||||
// dynamic linker will fill each slot with a pointer to the
|
||||
// corresponding x@plt entry point.
|
||||
//
|
||||
// 3) We generate the "call stub" x_stub (once per dynamic
|
||||
// function/object file pair). This saves the TOC in the
|
||||
// TOC save slot, reads the function pointer from x's .plt
|
||||
// slot and calls it like any other global entry point
|
||||
// (including setting r12 to the function address).
|
||||
// 3) We generate a "call stub" x_stub based on the properties
|
||||
// of the caller.
|
||||
//
|
||||
// 4) We generate the "symbol resolver stub" x@plt (once per
|
||||
// dynamic function). This is solely a branch to the glink
|
||||
@ -90,49 +88,65 @@ func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, s loader.Sym)
|
||||
// platforms and ppc64's .glink is like .plt on other
|
||||
// platforms.
|
||||
|
||||
// Find all R_PPC64_REL24 relocations that reference dynamic
|
||||
// imports. Reserve PLT entries for these symbols and
|
||||
// generate call stubs. The call stubs need to live in .text,
|
||||
// which is why we need to do this pass this early.
|
||||
//
|
||||
// This assumes "case 1" from the ABI, where the caller needs
|
||||
// us to save and restore the TOC pointer.
|
||||
// Find all relocations that reference dynamic imports.
|
||||
// Reserve PLT entries for these symbols and generate call
|
||||
// stubs. The call stubs need to live in .text, which is why we
|
||||
// need to do this pass this early.
|
||||
|
||||
// Reserve PLT entry and generate symbol
|
||||
// resolver
|
||||
// Reserve PLT entry and generate symbol resolver
|
||||
addpltsym(ctxt, ldr, r.Sym())
|
||||
|
||||
// Generate call stub. Important to note that we're looking
|
||||
// up the stub using the same version as the parent symbol (s),
|
||||
// needed so that symtoc() will select the right .TOC. symbol
|
||||
// when processing the stub. In older versions of the linker
|
||||
// this was done by setting stub.Outer to the parent, but
|
||||
// if the stub has the right version initially this is not needed.
|
||||
n := fmt.Sprintf("%s.%s", ldr.SymName(s), ldr.SymName(r.Sym()))
|
||||
stub := ldr.CreateSymForUpdate(n, ldr.SymVersion(s))
|
||||
// The stub types are described in gencallstub.
|
||||
stubType := 0
|
||||
stubTypeStr := ""
|
||||
|
||||
// For now, the choice of call stub type is determined by whether
|
||||
// the caller maintains a TOC pointer in R2. A TOC pointer implies
|
||||
// we can always generate a position independent stub.
|
||||
//
|
||||
// For dynamic calls made from an external object, it is safe to
|
||||
// assume a TOC pointer is maintained. These were imported from
|
||||
// a R_PPC64_REL24 relocation.
|
||||
//
|
||||
// For dynamic calls made from a Go object, the shared attribute
|
||||
// indicates a PIC symbol, which requires a TOC pointer be
|
||||
// maintained. Otherwise, a simpler non-PIC stub suffices.
|
||||
if ldr.AttrExternal(s) || ldr.AttrShared(s) {
|
||||
stubTypeStr = "_tocrel"
|
||||
stubType = 1
|
||||
} else {
|
||||
stubTypeStr = "_nopic"
|
||||
stubType = 3
|
||||
}
|
||||
n := fmt.Sprintf("_pltstub%s.%s", stubTypeStr, ldr.SymName(r.Sym()))
|
||||
|
||||
// When internal linking, all text symbols share the same toc pointer.
|
||||
stub := ldr.CreateSymForUpdate(n, 0)
|
||||
firstUse = stub.Size() == 0
|
||||
if firstUse {
|
||||
gencallstub(ctxt, ldr, 1, stub, r.Sym())
|
||||
gencallstub(ctxt, ldr, stubType, stub, r.Sym())
|
||||
}
|
||||
|
||||
// Update the relocation to use the call stub
|
||||
r.SetSym(stub.Sym())
|
||||
|
||||
// Make the symbol writeable so we can fixup toc.
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
su.MakeWritable()
|
||||
p := su.Data()
|
||||
su.SetRelocSym(ri, stub.Sym())
|
||||
|
||||
// Check for toc restore slot (a nop), and replace with toc restore.
|
||||
var nop uint32
|
||||
if len(p) >= int(r.Off()+8) {
|
||||
nop = ctxt.Arch.ByteOrder.Uint32(p[r.Off()+4:])
|
||||
// A type 1 call must restore the toc pointer after the call.
|
||||
if stubType == 1 {
|
||||
su.MakeWritable()
|
||||
p := su.Data()
|
||||
|
||||
// Check for a toc pointer restore slot (a nop), and rewrite to restore the toc pointer.
|
||||
var nop uint32
|
||||
if len(p) >= int(r.Off()+8) {
|
||||
nop = ctxt.Arch.ByteOrder.Uint32(p[r.Off()+4:])
|
||||
}
|
||||
if nop != 0x60000000 {
|
||||
ldr.Errorf(s, "Symbol %s is missing toc restoration slot at offset %d", ldr.SymName(s), r.Off()+4)
|
||||
}
|
||||
const o1 = 0xe8410018 // ld r2,24(r1)
|
||||
ctxt.Arch.ByteOrder.PutUint32(p[r.Off()+4:], o1)
|
||||
}
|
||||
if nop != 0x60000000 {
|
||||
ldr.Errorf(s, "Symbol %s is missing toc restoration slot at offset %d", ldr.SymName(s), r.Off()+4)
|
||||
}
|
||||
const o1 = 0xe8410018 // ld r2,24(r1)
|
||||
ctxt.Arch.ByteOrder.PutUint32(p[r.Off()+4:], o1)
|
||||
|
||||
return stub.Sym(), firstUse
|
||||
}
|
||||
@ -150,7 +164,7 @@ func genstubs(ctxt *ld.Link, ldr *loader.Loader) {
|
||||
switch ldr.SymType(r.Sym()) {
|
||||
case sym.SDYNIMPORT:
|
||||
// This call goes through the PLT, generate and call through a PLT stub.
|
||||
if sym, firstUse := genpltstub(ctxt, ldr, r, s); firstUse {
|
||||
if sym, firstUse := genpltstub(ctxt, ldr, r, i, s); firstUse {
|
||||
stubs = append(stubs, sym)
|
||||
}
|
||||
|
||||
@ -344,44 +358,38 @@ func gentext(ctxt *ld.Link, ldr *loader.Loader) {
|
||||
}
|
||||
}
|
||||
|
||||
// Construct a call stub in stub that calls symbol targ via its PLT
|
||||
// entry.
|
||||
func gencallstub(ctxt *ld.Link, ldr *loader.Loader, abicase int, stub *loader.SymbolBuilder, targ loader.Sym) {
|
||||
if abicase != 1 {
|
||||
// If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC
|
||||
// relocations, we'll need to implement cases 2 and 3.
|
||||
log.Fatalf("gencallstub only implements case 1 calls")
|
||||
}
|
||||
|
||||
// Create a calling stub. The stubType maps directly to the properties listed in the ELFv2 1.5
|
||||
// section 4.2.5.3.
|
||||
//
|
||||
// There are 3 cases today (as paraphrased from the ELFv2 document):
|
||||
//
|
||||
// 1. R2 holds the TOC pointer on entry. The call stub must save R2 into the ELFv2 TOC stack save slot.
|
||||
//
|
||||
// 2. R2 holds the TOC pointer on entry. The caller has already saved R2 to the TOC stack save slot.
|
||||
//
|
||||
// 3. R2 does not hold the TOC pointer on entry. The caller has no expectations of R2.
|
||||
//
|
||||
// Go only needs case 1 and 3 today. Go symbols which have AttrShare set could use case 2, but case 1 always
|
||||
// works in those cases too.
|
||||
func gencallstub(ctxt *ld.Link, ldr *loader.Loader, stubType int, stub *loader.SymbolBuilder, targ loader.Sym) {
|
||||
plt := ctxt.PLT
|
||||
|
||||
stub.SetType(sym.STEXT)
|
||||
|
||||
// Save TOC pointer in TOC save slot
|
||||
stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
|
||||
|
||||
// Load the function pointer from the PLT.
|
||||
rel, ri1 := stub.AddRel(objabi.R_POWER_TOC)
|
||||
rel.SetOff(int32(stub.Size()))
|
||||
rel.SetSiz(2)
|
||||
rel.SetAdd(int64(ldr.SymPlt(targ)))
|
||||
rel.SetSym(plt)
|
||||
if ctxt.Arch.ByteOrder == binary.BigEndian {
|
||||
rel.SetOff(rel.Off() + int32(rel.Siz()))
|
||||
switch stubType {
|
||||
case 1:
|
||||
// Save TOC, then load targ address from PLT using TOC.
|
||||
stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
|
||||
stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_TOCREL_DS, 8)
|
||||
stub.SetUint32(ctxt.Arch, stub.Size()-8, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
|
||||
stub.SetUint32(ctxt.Arch, stub.Size()-4, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
|
||||
case 3:
|
||||
// Load targ address from PLT. This is position dependent.
|
||||
stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_DS, 8)
|
||||
stub.SetUint32(ctxt.Arch, stub.Size()-8, 0x3d800000) // lis r12,targ@plt@ha
|
||||
stub.SetUint32(ctxt.Arch, stub.Size()-4, 0xe98c0000) // ld r12,targ@plt@l(r12)
|
||||
default:
|
||||
log.Fatalf("gencallstub does not support ELFv2 ABI property %d", stubType)
|
||||
}
|
||||
ldr.SetRelocVariant(stub.Sym(), int(ri1), sym.RV_POWER_HA)
|
||||
stub.AddUint32(ctxt.Arch, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
|
||||
|
||||
rel2, ri2 := stub.AddRel(objabi.R_POWER_TOC)
|
||||
rel2.SetOff(int32(stub.Size()))
|
||||
rel2.SetSiz(2)
|
||||
rel2.SetAdd(int64(ldr.SymPlt(targ)))
|
||||
rel2.SetSym(plt)
|
||||
if ctxt.Arch.ByteOrder == binary.BigEndian {
|
||||
rel2.SetOff(rel2.Off() + int32(rel2.Siz()))
|
||||
}
|
||||
ldr.SetRelocVariant(stub.Sym(), int(ri2), sym.RV_POWER_LO)
|
||||
stub.AddUint32(ctxt.Arch, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
|
||||
|
||||
// Jump to the loaded pointer
|
||||
stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
|
||||
|
Loading…
Reference in New Issue
Block a user