mirror of
https://github.com/golang/go
synced 2024-11-16 20:54:48 -07: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).
|
// The build configuration supports PC-relative instructions and relocations (limited to tested targets).
|
||||||
var hasPCrel = buildcfg.GOPPC64 >= 10 && buildcfg.GOOS == "linux"
|
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
|
// The ppc64 ABI PLT has similar concepts to other
|
||||||
// architectures, but is laid out quite differently. When we
|
// architectures, but is laid out quite differently. When we
|
||||||
// see an R_PPC64_REL24 relocation to a dynamic symbol
|
// see a relocation to a dynamic symbol (indicating that the
|
||||||
// (indicating that the call needs to go through the PLT), we
|
// call needs to go through the PLT), we generate up to three
|
||||||
// generate up to three stubs and reserve a PLT slot.
|
// stubs and reserve a PLT slot.
|
||||||
//
|
//
|
||||||
// 1) The call site will be bl x; nop (where the relocation
|
// 1) The call site is a "bl x" where genpltstub rewrites it to
|
||||||
// applies to the bl). We rewrite this to bl x_stub; ld
|
// "bl x_stub". Depending on the properties of the caller
|
||||||
// r2,24(r1). The ld is necessary because x_stub will save
|
// (see ELFv2 1.5 4.2.5.3), a nop may be expected immediately
|
||||||
// r2 (the TOC pointer) at 24(r1) (the "TOC save slot").
|
// 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
|
// 2) We reserve space for a pointer in the .plt section (once
|
||||||
// per referenced dynamic function). .plt is a data
|
// 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
|
// dynamic linker will fill each slot with a pointer to the
|
||||||
// corresponding x@plt entry point.
|
// corresponding x@plt entry point.
|
||||||
//
|
//
|
||||||
// 3) We generate the "call stub" x_stub (once per dynamic
|
// 3) We generate a "call stub" x_stub based on the properties
|
||||||
// function/object file pair). This saves the TOC in the
|
// of the caller.
|
||||||
// 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).
|
|
||||||
//
|
//
|
||||||
// 4) We generate the "symbol resolver stub" x@plt (once per
|
// 4) We generate the "symbol resolver stub" x@plt (once per
|
||||||
// dynamic function). This is solely a branch to the glink
|
// 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 and ppc64's .glink is like .plt on other
|
||||||
// platforms.
|
// platforms.
|
||||||
|
|
||||||
// Find all R_PPC64_REL24 relocations that reference dynamic
|
// Find all relocations that reference dynamic imports.
|
||||||
// imports. Reserve PLT entries for these symbols and
|
// Reserve PLT entries for these symbols and generate call
|
||||||
// generate call stubs. The call stubs need to live in .text,
|
// stubs. The call stubs need to live in .text, which is why we
|
||||||
// which is why we need to do this pass this early.
|
// 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.
|
|
||||||
|
|
||||||
// Reserve PLT entry and generate symbol
|
// Reserve PLT entry and generate symbol resolver
|
||||||
// resolver
|
|
||||||
addpltsym(ctxt, ldr, r.Sym())
|
addpltsym(ctxt, ldr, r.Sym())
|
||||||
|
|
||||||
// Generate call stub. Important to note that we're looking
|
// The stub types are described in gencallstub.
|
||||||
// up the stub using the same version as the parent symbol (s),
|
stubType := 0
|
||||||
// needed so that symtoc() will select the right .TOC. symbol
|
stubTypeStr := ""
|
||||||
// when processing the stub. In older versions of the linker
|
|
||||||
// this was done by setting stub.Outer to the parent, but
|
// For now, the choice of call stub type is determined by whether
|
||||||
// if the stub has the right version initially this is not needed.
|
// the caller maintains a TOC pointer in R2. A TOC pointer implies
|
||||||
n := fmt.Sprintf("%s.%s", ldr.SymName(s), ldr.SymName(r.Sym()))
|
// we can always generate a position independent stub.
|
||||||
stub := ldr.CreateSymForUpdate(n, ldr.SymVersion(s))
|
//
|
||||||
|
// 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
|
firstUse = stub.Size() == 0
|
||||||
if firstUse {
|
if firstUse {
|
||||||
gencallstub(ctxt, ldr, 1, stub, r.Sym())
|
gencallstub(ctxt, ldr, stubType, stub, r.Sym())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the relocation to use the call stub
|
// 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 := ldr.MakeSymbolUpdater(s)
|
||||||
su.MakeWritable()
|
su.SetRelocSym(ri, stub.Sym())
|
||||||
p := su.Data()
|
|
||||||
|
|
||||||
// Check for toc restore slot (a nop), and replace with toc restore.
|
// A type 1 call must restore the toc pointer after the call.
|
||||||
var nop uint32
|
if stubType == 1 {
|
||||||
if len(p) >= int(r.Off()+8) {
|
su.MakeWritable()
|
||||||
nop = ctxt.Arch.ByteOrder.Uint32(p[r.Off()+4:])
|
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
|
return stub.Sym(), firstUse
|
||||||
}
|
}
|
||||||
@ -150,7 +164,7 @@ func genstubs(ctxt *ld.Link, ldr *loader.Loader) {
|
|||||||
switch ldr.SymType(r.Sym()) {
|
switch ldr.SymType(r.Sym()) {
|
||||||
case sym.SDYNIMPORT:
|
case sym.SDYNIMPORT:
|
||||||
// This call goes through the PLT, generate and call through a PLT stub.
|
// 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)
|
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
|
// Create a calling stub. The stubType maps directly to the properties listed in the ELFv2 1.5
|
||||||
// entry.
|
// section 4.2.5.3.
|
||||||
func gencallstub(ctxt *ld.Link, ldr *loader.Loader, abicase int, stub *loader.SymbolBuilder, targ loader.Sym) {
|
//
|
||||||
if abicase != 1 {
|
// There are 3 cases today (as paraphrased from the ELFv2 document):
|
||||||
// If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC
|
//
|
||||||
// relocations, we'll need to implement cases 2 and 3.
|
// 1. R2 holds the TOC pointer on entry. The call stub must save R2 into the ELFv2 TOC stack save slot.
|
||||||
log.Fatalf("gencallstub only implements case 1 calls")
|
//
|
||||||
}
|
// 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
|
plt := ctxt.PLT
|
||||||
|
|
||||||
stub.SetType(sym.STEXT)
|
stub.SetType(sym.STEXT)
|
||||||
|
|
||||||
// Save TOC pointer in TOC save slot
|
switch stubType {
|
||||||
stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
|
case 1:
|
||||||
|
// Save TOC, then load targ address from PLT using TOC.
|
||||||
// Load the function pointer from the PLT.
|
stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
|
||||||
rel, ri1 := stub.AddRel(objabi.R_POWER_TOC)
|
stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_TOCREL_DS, 8)
|
||||||
rel.SetOff(int32(stub.Size()))
|
stub.SetUint32(ctxt.Arch, stub.Size()-8, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
|
||||||
rel.SetSiz(2)
|
stub.SetUint32(ctxt.Arch, stub.Size()-4, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
|
||||||
rel.SetAdd(int64(ldr.SymPlt(targ)))
|
case 3:
|
||||||
rel.SetSym(plt)
|
// Load targ address from PLT. This is position dependent.
|
||||||
if ctxt.Arch.ByteOrder == binary.BigEndian {
|
stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_DS, 8)
|
||||||
rel.SetOff(rel.Off() + int32(rel.Siz()))
|
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
|
// Jump to the loaded pointer
|
||||||
stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
|
stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
|
||||||
|
Loading…
Reference in New Issue
Block a user