From 2ac993107fcae841ca6e81ad75e88d8b5973a115 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 16 Oct 2015 15:54:10 +1300 Subject: [PATCH] cmd/internal/obj, cmd/link: access global data via GOT when dynlinking on ppc64le Change-Id: I79c60241df6c785f35371e70c777a7bd6e93571c Reviewed-on: https://go-review.googlesource.com/15968 Reviewed-by: Russ Cox --- src/cmd/internal/obj/link.go | 5 + src/cmd/internal/obj/ppc64/a.out.go | 1 + src/cmd/internal/obj/ppc64/anames9.go | 1 + src/cmd/internal/obj/ppc64/asm9.go | 18 ++++ src/cmd/internal/obj/ppc64/obj9.go | 127 ++++++++++++++++++++++++++ src/cmd/link/internal/ppc64/asm.go | 7 ++ 6 files changed, 159 insertions(+) diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index a7e0c2f863..ee0b4e9fa4 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -500,6 +500,11 @@ const ( // bits of the address are not 0. R_ADDRPOWER_DS + // R_ADDRPOWER_PCREL relocates a D-form, DS-form instruction sequence like + // R_ADDRPOWER_DS but inserts the offset of the GOT slot for the referenced symbol + // from the TOC rather than the symbol's address. + R_ADDRPOWER_GOT + // R_ADDRPOWER_PCREL relocates two D-form instructions like R_ADDRPOWER, but // inserts the displacement from the place being relocated to the address of the // the relocated symbol instead of just its address. diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go index d28e1e895e..4f6cdcde06 100644 --- a/src/cmd/internal/obj/ppc64/a.out.go +++ b/src/cmd/internal/obj/ppc64/a.out.go @@ -221,6 +221,7 @@ const ( C_ANY C_GOK C_ADDR + C_GOTADDR C_TLS_LE C_TLS_IE C_TEXTSIZE diff --git a/src/cmd/internal/obj/ppc64/anames9.go b/src/cmd/internal/obj/ppc64/anames9.go index 1b5d564dfe..d7140b1c8a 100644 --- a/src/cmd/internal/obj/ppc64/anames9.go +++ b/src/cmd/internal/obj/ppc64/anames9.go @@ -35,6 +35,7 @@ var cnames9 = []string{ "ANY", "GOK", "ADDR", + "GOTADDR", "TLS_LE", "TLS_IE", "TEXTSIZE", diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index 25b9aef830..25deeada42 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -248,6 +248,8 @@ var optab = []Optab{ {AMOVD, C_TLS_LE, C_NONE, C_NONE, C_REG, 79, 4, 0}, {AMOVD, C_TLS_IE, C_NONE, C_NONE, C_REG, 80, 8, 0}, + {AMOVD, C_GOTADDR, C_NONE, C_NONE, C_REG, 81, 8, 0}, + /* load constant */ {AMOVD, C_SECON, C_NONE, C_NONE, C_REG, 3, 4, REGSB}, {AMOVD, C_SACON, C_NONE, C_NONE, C_REG, 3, 4, REGSP}, @@ -598,6 +600,9 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int { } return C_LEXT + case obj.NAME_GOTREF: + return C_GOTADDR + case obj.NAME_AUTO: ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG { @@ -2503,6 +2508,19 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { rel.Sym = p.From.Sym rel.Type = obj.R_POWER_TLS_IE + case 81: + v := vregoff(ctxt, &p.To) + if v != 0 { + ctxt.Diag("invalid offset against GOT slot %v", p) + } + + o1 = AOP_IRR(OP_ADDIS, uint32(p.To.Reg), REG_R2, 0) + o2 = AOP_IRR(uint32(opload(ctxt, AMOVD)), uint32(p.To.Reg), uint32(p.To.Reg), 0) + rel := obj.Addrel(ctxt.Cursym) + rel.Off = int32(ctxt.Pc) + rel.Siz = 8 + rel.Sym = p.From.Sym + rel.Type = obj.R_ADDRPOWER_GOT } out[0] = o1 diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go index 389d6ac88d..a876b99a76 100644 --- a/src/cmd/internal/obj/ppc64/obj9.go +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -63,6 +63,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { s.Size = 4 p.From.Type = obj.TYPE_MEM p.From.Sym = s + p.From.Sym.Local = true p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } @@ -75,6 +76,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { s.Size = 8 p.From.Type = obj.TYPE_MEM p.From.Sym = s + p.From.Sym.Local = true p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } @@ -87,6 +89,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { s.Size = 8 p.From.Type = obj.TYPE_MEM p.From.Sym = s + p.From.Sym.Local = true p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } @@ -112,6 +115,130 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { p.As = AADD } } + if ctxt.Flag_dynlink { + rewriteToUseGot(ctxt, p) + } +} + +// Rewrite p, if necessary, to access global data via the global offset table. +func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { + if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { + // ADUFFxxx $offset + // becomes + // MOVD runtime.duffxxx@GOT, R12 + // ADD $offset, R12 + // MOVD R12, CTR + // BL (CTR) + var sym *obj.LSym + if p.As == obj.ADUFFZERO { + sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) + } else { + sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) + } + offset := p.To.Offset + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_GOTREF + p.From.Sym = sym + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R12 + p.To.Name = obj.NAME_NONE + p.To.Offset = 0 + p.To.Sym = nil + p1 := obj.Appendp(ctxt, p) + p1.As = AADD + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = offset + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REG_R12 + p2 := obj.Appendp(ctxt, p1) + p2.As = AMOVD + p2.From.Type = obj.TYPE_REG + p2.From.Reg = REG_R12 + p2.To.Type = obj.TYPE_REG + p2.To.Reg = REG_CTR + p3 := obj.Appendp(ctxt, p2) + p3.As = obj.ACALL + p3.From.Type = obj.TYPE_REG + p3.From.Reg = REG_R12 + p3.To.Type = obj.TYPE_REG + p3.To.Reg = REG_CTR + } + + // We only care about global data: NAME_EXTERN means a global + // symbol in the Go sense, and p.Sym.Local is true for a few + // internally defined symbols. + if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { + // MOVD $sym, Rx becomes MOVD sym@GOT, Rx + // MOVD $sym+, Rx becomes MOVD sym@GOT, Rx; ADD , Rx + if p.As != AMOVD { + ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) + } + if p.To.Type != obj.TYPE_REG { + ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) + } + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_GOTREF + if p.From.Offset != 0 { + q := obj.Appendp(ctxt, p) + q.As = AADD + q.From.Type = obj.TYPE_CONST + q.From.Offset = p.From.Offset + q.To = p.To + p.From.Offset = 0 + } + } + if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { + ctxt.Diag("don't know how to handle %v with -dynlink", p) + } + var source *obj.Addr + // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry + // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP) + // An addition may be inserted between the two MOVs if there is an offset. + if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { + if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { + ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) + } + source = &p.From + } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { + source = &p.To + } else { + return + } + if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { + return + } + if source.Sym.Type == obj.STLSBSS { + return + } + if source.Type != obj.TYPE_MEM { + ctxt.Diag("don't know how to handle %v with -dynlink", p) + } + p1 := obj.Appendp(ctxt, p) + p2 := obj.Appendp(ctxt, p1) + + p1.As = AMOVD + p1.From.Type = obj.TYPE_MEM + p1.From.Sym = source.Sym + p1.From.Name = obj.NAME_GOTREF + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REGTMP + + p2.As = p.As + p2.From = p.From + p2.To = p.To + if p.From.Name == obj.NAME_EXTERN { + p2.From.Reg = REGTMP + p2.From.Name = obj.NAME_NONE + p2.From.Sym = nil + } else if p.To.Name == obj.NAME_EXTERN { + p2.To.Reg = REGTMP + p2.To.Name = obj.NAME_NONE + p2.To.Sym = nil + } else { + return + } + obj.Nopout(p) } func preprocess(ctxt *obj.Link, cursym *obj.LSym) { diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 706160d0fb..c16cd3cf7a 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -334,6 +334,12 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int { ld.Thearch.Vput(uint64(sectoff + 4)) ld.Thearch.Vput(ld.R_PPC64_ADDR16_LO_DS | uint64(elfsym)<<32) + case obj.R_ADDRPOWER_GOT: + ld.Thearch.Vput(ld.R_PPC64_GOT16_HA | uint64(elfsym)<<32) + ld.Thearch.Vput(uint64(r.Xadd)) + ld.Thearch.Vput(uint64(sectoff + 4)) + ld.Thearch.Vput(ld.R_PPC64_GOT16_LO_DS | uint64(elfsym)<<32) + case obj.R_ADDRPOWER_PCREL: ld.Thearch.Vput(ld.R_PPC64_REL16_HA | uint64(elfsym)<<32) ld.Thearch.Vput(uint64(r.Xadd)) @@ -464,6 +470,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { obj.R_ADDRPOWER_DS, obj.R_ADDRPOWER_TOCREL, obj.R_ADDRPOWER_TOCREL_DS, + obj.R_ADDRPOWER_GOT, obj.R_ADDRPOWER_PCREL: r.Done = 0 -- 2.48.1