mirror of
https://github.com/golang/go
synced 2024-11-24 12:00:14 -07:00
cmd/internal/obj/x86: position independent access to global data on 386 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>
This commit is contained in:
parent
3c85e1b186
commit
cb0393866a
@ -2096,7 +2096,7 @@ func oclass(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
|
|||||||
|
|
||||||
case obj.NAME_EXTERN,
|
case obj.NAME_EXTERN,
|
||||||
obj.NAME_STATIC:
|
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 Yi32
|
||||||
}
|
}
|
||||||
return Yiauto // use pc-relative addressing
|
return Yiauto // use pc-relative addressing
|
||||||
@ -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 {
|
if a.Name == obj.NAME_GOTREF {
|
||||||
r.Siz = 4
|
r.Siz = 4
|
||||||
r.Type = obj.R_GOTPCREL
|
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.Siz = 4
|
||||||
r.Type = obj.R_ADDR
|
r.Type = obj.R_ADDR
|
||||||
} else {
|
} else {
|
||||||
@ -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 {
|
if !isextern(a.Sym) && p.Mode == 64 {
|
||||||
goto bad
|
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))
|
v = int32(vaddr(ctxt, p, a, &rel))
|
||||||
|
|
||||||
case obj.NAME_AUTO,
|
case obj.NAME_AUTO,
|
||||||
@ -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 {
|
if a.Sym == nil {
|
||||||
ctxt.Diag("bad addr: %v", p)
|
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))
|
v = int32(vaddr(ctxt, p, a, &rel))
|
||||||
|
|
||||||
case obj.NAME_AUTO,
|
case obj.NAME_AUTO,
|
||||||
@ -4550,14 +4558,22 @@ func asmins(ctxt *obj.Link, p *obj.Prog) {
|
|||||||
r.Off++
|
r.Off++
|
||||||
}
|
}
|
||||||
if r.Type == obj.R_PCREL {
|
if r.Type == obj.R_PCREL {
|
||||||
// PC-relative addressing is relative to the end of the instruction,
|
if p.Mode == 64 || p.As == obj.AJMP || p.As == obj.ACALL {
|
||||||
// but the relocations applied by the linker are relative to the end
|
// PC-relative addressing is relative to the end of the instruction,
|
||||||
// of the relocation. Because immediate instruction
|
// but the relocations applied by the linker are relative to the end
|
||||||
// arguments can follow the PC-relative memory reference in the
|
// of the relocation. Because immediate instruction
|
||||||
// instruction encoding, the two may not coincide. In this case,
|
// arguments can follow the PC-relative memory reference in the
|
||||||
// adjust addend so that linker can keep relocating relative to the
|
// instruction encoding, the two may not coincide. In this case,
|
||||||
// end of the relocation.
|
// adjust addend so that linker can keep relocating relative to the
|
||||||
r.Add -= p.Pc + int64(n) - (int64(r.Off) + int64(r.Siz))
|
// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +311,10 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
|
|||||||
if ctxt.Flag_dynlink {
|
if ctxt.Flag_dynlink {
|
||||||
rewriteToUseGot(ctxt, p)
|
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.
|
// Rewrite p, if necessary, to access global data via the global offset table.
|
||||||
@ -426,6 +430,67 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
|
|||||||
obj.Nopout(p)
|
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) {
|
func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
|
||||||
if p.As == ALEAL || p.As == ALEAQ {
|
if p.As == ALEAL || p.As == ALEAQ {
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user