Commit e6ceb92e authored by Michael Hudson-Doyle's avatar Michael Hudson-Doyle

cmd/internal/obj/arm: access global data via GOT on arm when -dynlink

Change-Id: I88034611f56cc06bb47b0c431075cc78ca8dbb09
Reviewed-on: https://go-review.googlesource.com/14188Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent 67faca7d
......@@ -981,6 +981,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
return C_NONE
case obj.TYPE_REG:
ctxt.Instoffset = 0
if REG_R0 <= a.Reg && a.Reg <= REG_R15 {
return C_REG
}
......@@ -1010,6 +1011,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
case obj.TYPE_MEM:
switch a.Name {
case obj.NAME_EXTERN,
obj.NAME_GOTREF,
obj.NAME_STATIC:
if a.Sym == nil || a.Sym.Name == "" {
fmt.Printf("null sym external\n")
......@@ -1130,6 +1132,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
return C_LCON
case obj.NAME_EXTERN,
obj.NAME_GOTREF,
obj.NAME_STATIC:
s := a.Sym
if s == nil {
......@@ -1589,7 +1592,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
aclass(ctxt, &p.To)
if ctxt.Instoffset != 0 {
ctxt.Diag("%v: doesn't support BL offset(REG) where offset != 0", p)
ctxt.Diag("%v: doesn't support BL offset(REG) with non-zero offset %d", p, ctxt.Instoffset)
}
o1 = oprrr(ctxt, ABL, int(p.Scond))
o1 |= (uint32(p.To.Reg) & 15) << 0
......@@ -1644,7 +1647,11 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
rel.Add = p.To.Offset
if ctxt.Flag_shared != 0 {
rel.Type = obj.R_PCREL
if p.To.Name == obj.NAME_GOTREF {
rel.Type = obj.R_GOTPCREL
} else {
rel.Type = obj.R_PCREL
}
rel.Add += ctxt.Pc - p.Rel.Pc - 8
} else {
rel.Type = obj.R_ADDR
......
......@@ -129,6 +129,123 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
p.From.Offset = 0
}
}
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
// MOVW runtime.duffxxx@GOT, R9
// ADD $offset, R9
// CALL (R9)
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 = AMOVW
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_R9
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_R9
p2 := obj.Appendp(ctxt, p1)
p2.As = obj.ACALL
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = REG_R9
return
}
// 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 {
// MOVW $sym, Rx becomes MOVW sym@GOT, Rx
// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
if p.As != AMOVW {
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 MOVW sym@GOT, R9; MOVx (R9), Ry
// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
// 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 = AMOVW
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_R9
p2.As = p.As
p2.From = p.From
p2.To = p.To
if p.From.Name == obj.NAME_EXTERN {
p2.From.Reg = REG_R9
p2.From.Name = obj.NAME_NONE
p2.From.Sym = nil
} else if p.To.Name == obj.NAME_EXTERN {
p2.To.Reg = REG_R9
p2.To.Name = obj.NAME_NONE
p2.To.Sym = nil
} else {
return
}
obj.Nopout(p)
}
// Prog.mark
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment