func nacladdr(ctxt *liblink.Link, p *liblink.Prog, a *liblink.Addr) { if p.As == ALEAL || p.As == ALEAQ { return } if a.Type_ == D_BP || a.Type_ == D_INDIR+D_BP { ctxt.Diag("invalid address: %P", p) return } if a.Type_ == D_INDIR+D_TLS { a.Type_ = D_INDIR + D_BP } else if a.Type_ == D_TLS { a.Type_ = D_BP } if D_INDIR <= a.Type_ && a.Type_ <= D_INDIR+D_INDIR { switch a.Type_ { // all ok case D_INDIR + D_BP, D_INDIR + D_SP, D_INDIR + D_R15: break default: if a.Index != D_NONE { ctxt.Diag("invalid address %P", p) } a.Index = uint8(a.Type_ - D_INDIR) if a.Index != D_NONE { a.Scale = 1 } a.Type_ = D_INDIR + D_R15 break } } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var tlsfallback *liblink.LSym p.From.Class = 0 p.To.Class = 0 // Rewrite B/BL to symbol as D_BRANCH. switch p.As { case AB, ABL, ADUFFZERO, ADUFFCOPY: if p.To.Type_ == D_OREG && (p.To.Name == D_EXTERN || p.To.Name == D_STATIC) && p.To.Sym != nil { p.To.Type_ = D_BRANCH } break } // Replace TLS register fetches on older ARM procesors. switch p.As { // Treat MRC 15, 0, <reg>, C13, C0, 3 specially. case AMRC: if p.To.Offset&0xffff0fff == 0xee1d0f70 { // Because the instruction might be rewriten to a BL which returns in R0 // the register must be zero. if p.To.Offset&0xf000 != 0 { ctxt.Diag("%L: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Lineno) } if ctxt.Goarm < 7 { // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. if tlsfallback == nil { tlsfallback = liblink.Linklookup(ctxt, "runtime.read_tls_fallback", 0) } // MOVW LR, R11 p.As = AMOVW p.From.Type_ = D_REG p.From.Reg = REGLINK p.To.Type_ = D_REG p.To.Reg = REGTMP // BL runtime.read_tls_fallback(SB) p = liblink.Appendp(ctxt, p) p.As = ABL p.To.Type_ = D_BRANCH p.To.Sym = tlsfallback p.To.Offset = 0 // MOVW R11, LR p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Type_ = D_REG p.From.Reg = REGTMP p.To.Type_ = D_REG p.To.Reg = REGLINK break } } // Otherwise, MRC/MCR instructions need no further treatment. p.As = AWORD break } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Type_ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", i32) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Type_ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Type_ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", i64) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Type_ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } break } if ctxt.Flag_shared != 0 { // Shared libraries use R_ARM_TLS_IE32 instead of // R_ARM_TLS_LE32, replacing the link time constant TLS offset in // runtime.tlsg with an address to a GOT entry containing the // offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to // compensate. if ctxt.Tlsg == nil { ctxt.Tlsg = liblink.Linklookup(ctxt, "runtime.tlsg", 0) } if p.From.Type_ == D_CONST && p.From.Name == D_EXTERN && p.From.Sym == ctxt.Tlsg { p.From.Type_ = D_OREG } if p.To.Type_ == D_CONST && p.To.Name == D_EXTERN && p.To.Sym == ctxt.Tlsg { p.To.Type_ = D_OREG } } }
func addstacksplit(ctxt *liblink.Link, cursym *liblink.LSym) { var p *liblink.Prog var q *liblink.Prog var p1 *liblink.Prog var p2 *liblink.Prog var q1 *liblink.Prog var o int var mov int var aoffset int var textstksiz int64 var textarg int64 var autosize int32 if ctxt.Symmorestack[0] == nil { ctxt.Symmorestack[0] = liblink.Linklookup(ctxt, "runtime.morestack", 0) ctxt.Symmorestack[1] = liblink.Linklookup(ctxt, "runtime.morestack_noctxt", 0) } // TODO(minux): add morestack short-cuts with small fixed frame-size. ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p = cursym.Text parsetextconst(p.To.Offset, &textstksiz, &textarg) cursym.Args = int32(p.To.Offset >> 32) cursym.Locals = int32(textstksiz) /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", liblink.Cputime()) } liblink.Bflush(ctxt.Bso) q = nil for p = cursym.Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case ATEXT: q = p p.Mark |= LABEL | LEAF | SYNC if p.Link != nil { p.Link.Mark |= LABEL } case ANOR: q = p if p.To.Type_ == D_REG { if p.To.Reg == REGZERO { p.Mark |= LABEL | SYNC } } case ALWAR, ASTWCCC, AECIWX, AECOWX, AEIEIO, AICBI, AISYNC, ATLBIE, ATLBIEL, ASLBIA, ASLBIE, ASLBMFEE, ASLBMFEV, ASLBMTE, ADCBF, ADCBI, ADCBST, ADCBT, ADCBTST, ADCBZ, ASYNC, ATLBSYNC, APTESYNC, ATW, AWORD, ARFI, ARFCI, ARFID, AHRFID: q = p p.Mark |= LABEL | SYNC continue case AMOVW, AMOVWZ, AMOVD: q = p switch p.From.Type_ { case D_MSR, D_SPR, D_FPSCR, D_CREG, D_DCR: p.Mark |= LABEL | SYNC } switch p.To.Type_ { case D_MSR, D_SPR, D_FPSCR, D_CREG, D_DCR: p.Mark |= LABEL | SYNC } continue case AFABS, AFABSCC, AFADD, AFADDCC, AFCTIW, AFCTIWCC, AFCTIWZ, AFCTIWZCC, AFDIV, AFDIVCC, AFMADD, AFMADDCC, AFMOVD, AFMOVDU, /* case AFMOVDS: */ AFMOVS, AFMOVSU, /* case AFMOVSD: */ AFMSUB, AFMSUBCC, AFMUL, AFMULCC, AFNABS, AFNABSCC, AFNEG, AFNEGCC, AFNMADD, AFNMADDCC, AFNMSUB, AFNMSUBCC, AFRSP, AFRSPCC, AFSUB, AFSUBCC: q = p p.Mark |= FLOAT continue case ABL, ABCL, ADUFFZERO, ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case ABC, ABEQ, ABGE, ABGT, ABLE, ABLT, ABNE, ABR, ABVC, ABVS: p.Mark |= BRANCH q = p q1 = p.Pcond if q1 != nil { for q1.As == ANOP { q1 = q1.Link p.Pcond = q1 } if !(q1.Mark&LEAF != 0) { q1.Mark |= LABEL } } else { p.Mark |= LABEL } q1 = p.Link if q1 != nil { q1.Mark |= LABEL } continue case AFCMPO, AFCMPU: q = p p.Mark |= FCMP | FLOAT continue case ARETURN: q = p if p.Link != nil { p.Link.Mark |= LABEL } continue case ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ q1.Mark |= p.Mark continue default: q = p continue } } autosize = 0 for p = cursym.Text; p != nil; p = p.Link { o = int(p.As) switch o { case ATEXT: mov = AMOVD aoffset = 0 autosize = int32(textstksiz + 8) if (p.Mark&LEAF != 0) && autosize <= 8 { autosize = 0 } else if autosize&4 != 0 { autosize += 4 } p.To.Offset = int64(uint64(p.To.Offset)&(0xffffffff<<32) | uint64(uint32(autosize-8))) if !(p.Reg&liblink.NOSPLIT != 0) { p = stacksplit(ctxt, p, autosize, bool2int(!(cursym.Text.Reg&liblink.NEEDCTXT != 0))) // emit split check } q = p if autosize != 0 { /* use MOVDU to adjust R1 when saving R31, if autosize is small */ if !(cursym.Text.Mark&LEAF != 0) && autosize >= -BIG && autosize <= BIG { mov = AMOVDU aoffset = int(-autosize) } else { q = liblink.Appendp(ctxt, p) q.As = AADD q.Lineno = p.Lineno q.From.Type_ = D_CONST q.From.Offset = int64(-autosize) q.To.Type_ = D_REG q.To.Reg = REGSP q.Spadj = +autosize } } else if !(cursym.Text.Mark&LEAF != 0) { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) liblink.Bflush(ctxt.Bso) } cursym.Text.Mark |= LEAF } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 break } q = liblink.Appendp(ctxt, q) q.As = AMOVD q.Lineno = p.Lineno q.From.Type_ = D_SPR q.From.Offset = D_LR q.To.Type_ = D_REG q.To.Reg = REGTMP q = liblink.Appendp(ctxt, q) q.As = int16(mov) q.Lineno = p.Lineno q.From.Type_ = D_REG q.From.Reg = REGTMP q.To.Type_ = D_OREG q.To.Offset = int64(aoffset) q.To.Reg = REGSP if q.As == AMOVDU { q.Spadj = int32(-aoffset) } if cursym.Text.Reg&liblink.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 // CMP R0, R3 // BEQ end // MOVD panic_argp(R3), R4 // ADD $(autosize+8), R1, R5 // CMP R4, R5 // BNE end // ADD $8, R1, R6 // MOVD R6, panic_argp(R3) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes. q = liblink.Appendp(ctxt, q) q.As = AMOVD q.From.Type_ = D_OREG q.From.Reg = REGG q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic q.To.Type_ = D_REG q.To.Reg = 3 q = liblink.Appendp(ctxt, q) q.As = ACMP q.From.Type_ = D_REG q.From.Reg = 0 q.To.Type_ = D_REG q.To.Reg = 3 q = liblink.Appendp(ctxt, q) q.As = ABEQ q.To.Type_ = D_BRANCH p1 = q q = liblink.Appendp(ctxt, q) q.As = AMOVD q.From.Type_ = D_OREG q.From.Reg = 3 q.From.Offset = 0 // Panic.argp q.To.Type_ = D_REG q.To.Reg = 4 q = liblink.Appendp(ctxt, q) q.As = AADD q.From.Type_ = D_CONST q.From.Offset = int64(autosize) + 8 q.Reg = REGSP q.To.Type_ = D_REG q.To.Reg = 5 q = liblink.Appendp(ctxt, q) q.As = ACMP q.From.Type_ = D_REG q.From.Reg = 4 q.To.Type_ = D_REG q.To.Reg = 5 q = liblink.Appendp(ctxt, q) q.As = ABNE q.To.Type_ = D_BRANCH p2 = q q = liblink.Appendp(ctxt, q) q.As = AADD q.From.Type_ = D_CONST q.From.Offset = 8 q.Reg = REGSP q.To.Type_ = D_REG q.To.Reg = 6 q = liblink.Appendp(ctxt, q) q.As = AMOVD q.From.Type_ = D_REG q.From.Reg = 6 q.To.Type_ = D_OREG q.To.Reg = 3 q.To.Offset = 0 // Panic.argp q = liblink.Appendp(ctxt, q) q.As = ANOP p1.Pcond = q p2.Pcond = q } case ARETURN: if p.From.Type_ == D_CONST { ctxt.Diag("using BECOME (%P) is not supported!", p) break } if p.To.Sym != nil { // retjmp p.As = ABR p.To.Type_ = D_BRANCH break } if cursym.Text.Mark&LEAF != 0 { if !(autosize != 0) { p.As = ABR p.From = zprg.From p.To.Type_ = D_SPR p.To.Offset = D_LR p.Mark |= BRANCH break } p.As = AADD p.From.Type_ = D_CONST p.From.Offset = int64(autosize) p.To.Type_ = D_REG p.To.Reg = REGSP p.Spadj = -autosize q = ctxt.Arch.Prg() q.As = ABR q.Lineno = p.Lineno q.To.Type_ = D_SPR q.To.Offset = D_LR q.Mark |= BRANCH q.Spadj = +autosize q.Link = p.Link p.Link = q break } p.As = AMOVD p.From.Type_ = D_OREG p.From.Offset = 0 p.From.Reg = REGSP p.To.Type_ = D_REG p.To.Reg = REGTMP q = ctxt.Arch.Prg() q.As = AMOVD q.Lineno = p.Lineno q.From.Type_ = D_REG q.From.Reg = REGTMP q.To.Type_ = D_SPR q.To.Offset = D_LR q.Link = p.Link p.Link = q p = q if false { // Debug bad returns q = ctxt.Arch.Prg() q.As = AMOVD q.Lineno = p.Lineno q.From.Type_ = D_OREG q.From.Offset = 0 q.From.Reg = REGTMP q.To.Type_ = D_REG q.To.Reg = REGTMP q.Link = p.Link p.Link = q p = q } if autosize != 0 { q = ctxt.Arch.Prg() q.As = AADD q.Lineno = p.Lineno q.From.Type_ = D_CONST q.From.Offset = int64(autosize) q.To.Type_ = D_REG q.To.Reg = REGSP q.Spadj = -autosize q.Link = p.Link p.Link = q } q1 = ctxt.Arch.Prg() q1.As = ABR q1.Lineno = p.Lineno q1.To.Type_ = D_SPR q1.To.Offset = D_LR q1.Mark |= BRANCH q1.Spadj = +autosize q1.Link = q.Link q.Link = q1 case AADD: if p.To.Type_ == D_REG && p.To.Reg == REGSP && p.From.Type_ == D_CONST { p.Spadj = int32(-p.From.Offset) } break } } }
func addstacksplit(ctxt *liblink.Link, cursym *liblink.LSym) { var p *liblink.Prog var q *liblink.Prog var p1 *liblink.Prog var p2 *liblink.Prog var autoffset int32 var deltasp int32 var a int if ctxt.Symmorestack[0] == nil { ctxt.Symmorestack[0] = liblink.Linklookup(ctxt, "runtime.morestack", 0) ctxt.Symmorestack[1] = liblink.Linklookup(ctxt, "runtime.morestack_noctxt", 0) } if ctxt.Headtype == liblink.Hplan9 && ctxt.Plan9privates == nil { ctxt.Plan9privates = liblink.Linklookup(ctxt, "_privates", 0) } ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p = cursym.Text autoffset = int32(p.To.Offset) if autoffset < 0 { autoffset = 0 } cursym.Locals = autoffset cursym.Args = p.To.Offset2 q = nil if !(p.From.Scale&liblink.NOSPLIT != 0) || (p.From.Scale&liblink.WRAPPER != 0) { p = liblink.Appendp(ctxt, p) p = load_g_cx(ctxt, p) // load g into CX } if !(cursym.Text.From.Scale&liblink.NOSPLIT != 0) { p = stacksplit(ctxt, p, autoffset, bool2int(!(cursym.Text.From.Scale&liblink.NEEDCTXT != 0)), &q) // emit split check } if autoffset != 0 { p = liblink.Appendp(ctxt, p) p.As = AADJSP p.From.Type_ = D_CONST p.From.Offset = int64(autoffset) p.Spadj = autoffset } else { // zero-byte stack adjustment. // Insert a fake non-zero adjustment so that stkcheck can // recognize the end of the stack-splitting prolog. p = liblink.Appendp(ctxt, p) p.As = ANOP p.Spadj = int32(-ctxt.Arch.Ptrsize) p = liblink.Appendp(ctxt, p) p.As = ANOP p.Spadj = int32(ctxt.Arch.Ptrsize) } if q != nil { q.Pcond = p } deltasp = autoffset if cursym.Text.From.Scale&liblink.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVL g_panic(CX), BX // TESTL BX, BX // JEQ end // LEAL (autoffset+4)(SP), DI // CMPL panic_argp(BX), DI // JNE end // MOVL SP, panic_argp(BX) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Type_ = D_INDIR + D_CX p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic p.To.Type_ = D_BX p = liblink.Appendp(ctxt, p) p.As = ATESTL p.From.Type_ = D_BX p.To.Type_ = D_BX p = liblink.Appendp(ctxt, p) p.As = AJEQ p.To.Type_ = D_BRANCH p1 = p p = liblink.Appendp(ctxt, p) p.As = ALEAL p.From.Type_ = D_INDIR + D_SP p.From.Offset = int64(autoffset) + 4 p.To.Type_ = D_DI p = liblink.Appendp(ctxt, p) p.As = ACMPL p.From.Type_ = D_INDIR + D_BX p.From.Offset = 0 // Panic.argp p.To.Type_ = D_DI p = liblink.Appendp(ctxt, p) p.As = AJNE p.To.Type_ = D_BRANCH p2 = p p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Type_ = D_SP p.To.Type_ = D_INDIR + D_BX p.To.Offset = 0 // Panic.argp p = liblink.Appendp(ctxt, p) p.As = ANOP p1.Pcond = p p2.Pcond = p } if ctxt.Debugzerostack != 0 && autoffset != 0 && !(cursym.Text.From.Scale&liblink.NOSPLIT != 0) { // 8l -Z means zero the stack frame on entry. // This slows down function calls but can help avoid // false positives in garbage collection. p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Type_ = D_SP p.To.Type_ = D_DI p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Type_ = D_CONST p.From.Offset = int64(autoffset) / 4 p.To.Type_ = D_CX p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Type_ = D_CONST p.From.Offset = 0 p.To.Type_ = D_AX p = liblink.Appendp(ctxt, p) p.As = AREP p = liblink.Appendp(ctxt, p) p.As = ASTOSL } for ; p != nil; p = p.Link { a = int(p.From.Type_) if a == D_AUTO { p.From.Offset += int64(deltasp) } if a == D_PARAM { p.From.Offset += int64(deltasp) + 4 } a = int(p.To.Type_) if a == D_AUTO { p.To.Offset += int64(deltasp) } if a == D_PARAM { p.To.Offset += int64(deltasp) + 4 } switch p.As { default: continue case APUSHL, APUSHFL: deltasp += 4 p.Spadj = 4 continue case APUSHW, APUSHFW: deltasp += 2 p.Spadj = 2 continue case APOPL, APOPFL: deltasp -= 4 p.Spadj = -4 continue case APOPW, APOPFW: deltasp -= 2 p.Spadj = -2 continue case ARET: break } if autoffset != deltasp { ctxt.Diag("unbalanced PUSH/POP") } if autoffset != 0 { p.As = AADJSP p.From.Type_ = D_CONST p.From.Offset = int64(-autoffset) p.Spadj = -autoffset p = liblink.Appendp(ctxt, p) p.As = ARET // If there are instructions following // this ARET, they come from a branch // with the same stackframe, so undo // the cleanup. p.Spadj = +autoffset } if p.To.Sym != nil { // retjmp p.As = AJMP } } }