Skip to content

Commit

Permalink
cmd/internal/obj/x86: position independent access to global data on 3…
Browse files Browse the repository at this point in the history
…86 when -shared

This works by adding a call to __x86.get_pc_thunk.cx immediately before any
instruction that accesses global data and then assembling the instruction to
use the appropriate offset from CX instead of the absolute address. Some forms
cannot be assembled that way and are rewritten to load the address into CX
first.

-buildmode=pie works now, but is not yet tested.

Fixes #13201 (I think)

Change-Id: I32a8561e7fc9dd4ca6ae3b0e57ad78a6c50bf1f5
Reviewed-on: https://go-review.googlesource.com/17014
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
mwhudson committed Nov 18, 2015
1 parent 3c85e1b commit cb03938
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 12 deletions.
40 changes: 28 additions & 12 deletions src/cmd/internal/obj/x86/asm6.go
Original file line number Diff line number Diff line change
Expand Up @@ -2096,7 +2096,7 @@ func oclass(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {

case obj.NAME_EXTERN,
obj.NAME_STATIC:
if a.Sym != nil && isextern(a.Sym) || p.Mode == 32 {
if a.Sym != nil && isextern(a.Sym) || (p.Mode == 32 && ctxt.Flag_shared == 0) {
return Yi32
}
return Yiauto // use pc-relative addressing
Expand Down Expand Up @@ -2515,7 +2515,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
if a.Name == obj.NAME_GOTREF {
r.Siz = 4
r.Type = obj.R_GOTPCREL
} else if isextern(s) || p.Mode != 64 {
} else if isextern(s) || (p.Mode != 64 && ctxt.Flag_shared == 0) {
r.Siz = 4
r.Type = obj.R_ADDR
} else {
Expand Down Expand Up @@ -2592,7 +2592,11 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
if !isextern(a.Sym) && p.Mode == 64 {
goto bad
}
base = REG_NONE
if p.Mode == 32 && ctxt.Flag_shared != 0 {
base = REG_CX
} else {
base = REG_NONE
}
v = int32(vaddr(ctxt, p, a, &rel))

case obj.NAME_AUTO,
Expand Down Expand Up @@ -2638,7 +2642,11 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
if a.Sym == nil {
ctxt.Diag("bad addr: %v", p)
}
base = REG_NONE
if p.Mode == 32 && ctxt.Flag_shared != 0 {
base = REG_CX
} else {
base = REG_NONE
}
v = int32(vaddr(ctxt, p, a, &rel))

case obj.NAME_AUTO,
Expand Down Expand Up @@ -4550,14 +4558,22 @@ func asmins(ctxt *obj.Link, p *obj.Prog) {
r.Off++
}
if r.Type == obj.R_PCREL {
// PC-relative addressing is relative to the end of the instruction,
// but the relocations applied by the linker are relative to the end
// of the relocation. Because immediate instruction
// arguments can follow the PC-relative memory reference in the
// instruction encoding, the two may not coincide. In this case,
// adjust addend so that linker can keep relocating relative to the
// end of the relocation.
r.Add -= p.Pc + int64(n) - (int64(r.Off) + int64(r.Siz))
if p.Mode == 64 || p.As == obj.AJMP || p.As == obj.ACALL {
// PC-relative addressing is relative to the end of the instruction,
// but the relocations applied by the linker are relative to the end
// of the relocation. Because immediate instruction
// arguments can follow the PC-relative memory reference in the
// instruction encoding, the two may not coincide. In this case,
// adjust addend so that linker can keep relocating relative to the
// end of the relocation.
r.Add -= p.Pc + int64(n) - (int64(r.Off) + int64(r.Siz))
} else if p.Mode == 32 {
// On 386 PC-relative addressing (for non-call/jmp instructions)
// assumes that the previous instruction loaded the PC of the end
// of that instruction into CX, so the adjustment is relative to
// that.
r.Add += int64(r.Off) - p.Pc + int64(r.Siz)
}
}
}

Expand Down
65 changes: 65 additions & 0 deletions src/cmd/internal/obj/x86/obj6.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
if ctxt.Flag_dynlink {
rewriteToUseGot(ctxt, p)
}

if ctxt.Flag_shared != 0 && p.Mode == 32 {
rewriteToPcrel(ctxt, p)
}
}

// Rewrite p, if necessary, to access global data via the global offset table.
Expand Down Expand Up @@ -426,6 +430,67 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
obj.Nopout(p)
}

func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
// RegTo2 is set on the instructions we insert here so they don't get
// processed twice.
if p.RegTo2 != 0 {
return
}
if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
return
}
// Any Prog (aside from the above special cases) with an Addr with Name ==
// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx
// inserted before it.
isName := func(a *obj.Addr) bool {
if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
return false
}
if a.Sym.Type == obj.STLSBSS {
return false
}
return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
}

if isName(&p.From) && p.From.Type == obj.TYPE_ADDR {
// Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting
// to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX"
// respectively.
if p.To.Type != obj.TYPE_REG {
q := obj.Appendp(ctxt, p)
q.As = p.As
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_CX
q.To = p.To
p.As = AMOVL
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_CX
p.To.Sym = nil
p.To.Name = obj.NAME_NONE
}
}

if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
return
}
q := obj.Appendp(ctxt, p)
q.RegTo2 = 1
r := obj.Appendp(ctxt, q)
r.RegTo2 = 1
q.As = obj.ACALL
q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
q.To.Type = obj.TYPE_MEM
q.To.Name = obj.NAME_EXTERN
q.To.Sym.Local = true
r.As = p.As
r.Scond = p.Scond
r.From = p.From
r.From3 = p.From3
r.Reg = p.Reg
r.To = p.To
obj.Nopout(p)
}

func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
if p.As == ALEAL || p.As == ALEAQ {
return
Expand Down

0 comments on commit cb03938

Please sign in to comment.