From: Paul E. Murphy Date: Mon, 24 Apr 2023 20:59:17 +0000 (-0500) Subject: cmd/link/internal/ppc64: support non-PIC PLT call stubs X-Git-Tag: go1.21rc1~695 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f742ddc349723667fc9af5d0f16233f7762aeaa0;p=gostls13.git 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 TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui Run-TryBot: Paul Murphy Reviewed-by: Lynn Boger Reviewed-by: Carlos Amedee --- diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 121fbf8fa5..b77ee500fc 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -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:]) - } - if nop != 0x60000000 { - ldr.Errorf(s, "Symbol %s is missing toc restoration slot at offset %d", ldr.SymName(s), 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) } - 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())) - } - 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())) + 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(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