return C_NONE
case obj.TYPE_REG:
+ ctxt.Instoffset = 0
if REG_R0 <= a.Reg && a.Reg <= REG_R15 {
return C_REG
}
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")
return C_LCON
case obj.NAME_EXTERN,
+ obj.NAME_GOTREF,
obj.NAME_STATIC:
s := a.Sym
if s == nil {
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
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
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