From 342f17eaf75210cf0992f1f2fc1cc15c2780d97c Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 18 Nov 2015 12:30:23 +1300 Subject: [PATCH] cmd/internal/obj/x86, cmd/link: enable access to global data via GOT when -dynlink on 386 Change-Id: I97504a11291ee60e656efb7704e37387e864d74f Reviewed-on: https://go-review.googlesource.com/16385 Reviewed-by: Ian Lance Taylor --- src/cmd/internal/obj/x86/asm6.go | 5 ++ src/cmd/internal/obj/x86/obj6.go | 119 ++++++++++++++++++++++++------- src/cmd/link/internal/x86/asm.go | 25 ++++++- 3 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 219d29c44a6..04ef3b343f1 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -4575,6 +4575,11 @@ func asmins(ctxt *obj.Link, p *obj.Prog) { r.Add += int64(r.Off) - p.Pc + int64(r.Siz) } } + if r.Type == obj.R_GOTPCREL && p.Mode == 32 { + // On 386, R_GOTPCREL makes the same assumptions as R_PCREL. + r.Add += int64(r.Off) - p.Pc + int64(r.Siz) + } + } if p.Mode == 64 && ctxt.Headtype == obj.Hnacl && p.As != ACMPL && p.As != ACMPQ && p.To.Type == obj.TYPE_REG { diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index c6e93a6902b..ef22e55cd88 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -319,12 +319,25 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { // Rewrite p, if necessary, to access global data via the global offset table. func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { + var add, lea, mov, reg int16 + if p.Mode == 64 { + add = AADDQ + lea = ALEAQ + mov = AMOVQ + reg = REG_R15 + } else { + add = AADDL + lea = ALEAL + mov = AMOVL + reg = REG_CX + } + if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { // ADUFFxxx $offset // becomes - // MOVQ runtime.duffxxx@GOT, R15 - // ADDQ $offset, R15 - // CALL R15 + // $MOV runtime.duffxxx@GOT, $reg + // $ADD $offset, $reg + // CALL $reg var sym *obj.LSym if p.As == obj.ADUFFZERO { sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) @@ -332,60 +345,78 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) } offset := p.To.Offset - p.As = AMOVQ + p.As = mov 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_R15 + p.To.Reg = reg p.To.Offset = 0 p.To.Sym = nil p1 := obj.Appendp(ctxt, p) - p1.As = AADDQ + p1.As = add p1.From.Type = obj.TYPE_CONST p1.From.Offset = offset p1.To.Type = obj.TYPE_REG - p1.To.Reg = REG_R15 + p1.To.Reg = reg p2 := obj.Appendp(ctxt, p1) p2.As = obj.ACALL p2.To.Type = obj.TYPE_REG - p2.To.Reg = REG_R15 + p2.To.Reg = reg } // 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.As == ALEAQ && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { - // LEAQ sym, Rx becomes MOVQ $sym, Rx which will be rewritten below - p.As = AMOVQ + if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { + // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below + p.As = mov p.From.Type = obj.TYPE_ADDR } if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { - // MOVQ $sym, Rx becomes MOVQ sym@GOT, Rx - // MOVQ $sym+, Rx becomes MOVQ sym@GOT, Rx; ADDQ , Rx - if p.As != AMOVQ { - 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) + // $MOV $sym, Rx becomes $MOV sym@GOT, Rx + // $MOV $sym+, Rx becomes $MOV sym@GOT, Rx; $ADD , Rx + // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX + cmplxdest := false + pAs := p.As + var dest obj.Addr + if p.To.Type != obj.TYPE_REG || pAs != mov { + if p.Mode == 64 { + ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) + } + cmplxdest = true + dest = p.To + p.As = mov + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_CX + p.To.Sym = nil + p.To.Name = obj.NAME_NONE } p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_GOTREF + q := p if p.From.Offset != 0 { - q := obj.Appendp(ctxt, p) - q.As = AADDQ + q = obj.Appendp(ctxt, p) + q.As = add q.From.Type = obj.TYPE_CONST q.From.Offset = p.From.Offset q.To = p.To p.From.Offset = 0 } + if cmplxdest { + q = obj.Appendp(ctxt, q) + q.As = pAs + q.To = dest + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_CX + } } 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 MOVW sym@GOT, R15; MOVx (R15), Ry - // MOVx Ry, sym becomes MOVW sym@GOT, R15; MOVx Ry, (R15) + // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry + // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15) // 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 { @@ -397,7 +428,41 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { } else { return } - if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { + if p.As == obj.ACALL { + // When dynlinking on 386, almost any call might end up being a call + // to a PLT, so make sure the GOT pointer is loaded into BX. + // RegTo2 is set on the replacement call insn to stop it being + // processed when it is in turn passed to progedit. + if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 { + return + } + p1 := obj.Appendp(ctxt, p) + p2 := obj.Appendp(ctxt, p1) + + p1.As = ALEAL + p1.From.Type = obj.TYPE_MEM + p1.From.Name = obj.NAME_STATIC + p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0) + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REG_BX + + p2.As = p.As + p2.Scond = p.Scond + p2.From = p.From + p2.From3 = p.From3 + p2.Reg = p.Reg + p2.To = p.To + // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr + // in ../pass.go complain, so set it back to TYPE_MEM here, until p2 + // itself gets passed to progedit. + p2.To.Type = obj.TYPE_MEM + p2.RegTo2 = 1 + + obj.Nopout(p) + return + + } + if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP { return } if source.Type != obj.TYPE_MEM { @@ -406,22 +471,22 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { p1 := obj.Appendp(ctxt, p) p2 := obj.Appendp(ctxt, p1) - p1.As = AMOVQ + p1.As = mov 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 = REG_R15 + p1.To.Reg = reg p2.As = p.As p2.From = p.From p2.To = p.To if p.From.Name == obj.NAME_EXTERN { - p2.From.Reg = REG_R15 + p2.From.Reg = reg p2.From.Name = obj.NAME_NONE p2.From.Sym = nil } else if p.To.Name == obj.NAME_EXTERN { - p2.To.Reg = REG_R15 + p2.To.Reg = reg p2.To.Name = obj.NAME_NONE p2.To.Sym = nil } else { diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 603aaa4ff1a..5d214267fbe 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -267,8 +267,29 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int { return -1 } - case obj.R_CALL, - obj.R_PCREL: + case obj.R_GOTPCREL: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_GOTPC) + if r.Xsym.Name != "_GLOBAL_OFFSET_TABLE_" { + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(ld.R_386_GOT32 | uint32(elfsym)<<8) + } + } else { + return -1 + } + + case obj.R_CALL: + if r.Siz == 4 { + if r.Xsym.Type == obj.SDYNIMPORT { + ld.Thearch.Lput(ld.R_386_PLT32 | uint32(elfsym)<<8) + } else { + ld.Thearch.Lput(ld.R_386_PC32 | uint32(elfsym)<<8) + } + } else { + return -1 + } + + case obj.R_PCREL: if r.Siz == 4 { ld.Thearch.Lput(ld.R_386_PC32 | uint32(elfsym)<<8) } else {