diff options
Diffstat (limited to 'src/cmd/5l/noop.c')
-rw-r--r-- | src/cmd/5l/noop.c | 484 |
1 files changed, 327 insertions, 157 deletions
diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 99a096a31..305ed684e 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -32,18 +32,16 @@ #include "l.h" #include "../ld/lib.h" - -// see ../../runtime/proc.c:/StackGuard -enum -{ - StackBig = 4096, - StackSmall = 128, -}; +#include "../../pkg/runtime/stack.h" static Sym* sym_div; static Sym* sym_divu; static Sym* sym_mod; static Sym* sym_modu; +static Sym* symmorestack; +static Prog* pmorestack; + +static Prog* stacksplit(Prog*, int32); static void linkcase(Prog *casep) @@ -62,16 +60,16 @@ linkcase(Prog *casep) void noops(void) { - Prog *p, *q, *q1; + Prog *p, *q, *q1, *q2; int o; - Prog *pmorestack; - Sym *symmorestack; + Sym *tlsfallback, *gmsym; /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo + * fixup TLS */ if(debug['v']) @@ -86,6 +84,10 @@ noops(void) pmorestack = symmorestack->text; pmorestack->reg |= NOSPLIT; + tlsfallback = lookup("runtime.read_tls_fallback", 0); + gmsym = S; + if(linkmode == LinkExternal) + gmsym = lookup("runtime.tlsgm", 0); q = P; for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { @@ -150,6 +152,82 @@ noops(void) } } break; + case AWORD: + // Rewrite TLS register fetch: MRC 15, 0, <reg>, C13, C0, 3 + if((p->to.offset & 0xffff0fff) == 0xee1d0f70) { + if(HEADTYPE == Hopenbsd) { + p->as = ARET; + } else if(goarm < 7) { + if(tlsfallback->type != STEXT) { + diag("runtime·read_tls_fallback not defined"); + errorexit(); + } + // BL runtime.read_tls_fallback(SB) + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = tlsfallback; + p->cond = tlsfallback->text; + p->to.offset = 0; + cursym->text->mark &= ~LEAF; + } + if(linkmode == LinkExternal) { + // runtime.tlsgm is relocated with R_ARM_TLS_LE32 + // and $runtime.tlsgm will contain the TLS offset. + // + // MOV $runtime.tlsgm+tlsoffset(SB), REGTMP + // ADD REGTMP, <reg> + // + // In shared mode, runtime.tlsgm is relocated with + // R_ARM_TLS_IE32 and runtime.tlsgm(SB) will point + // to the GOT entry containing the TLS offset. + // + // MOV runtime.tlsgm(SB), REGTMP + // ADD REGTMP, <reg> + // SUB -tlsoffset, <reg> + // + // The SUB compensates for tlsoffset + // used in runtime.save_gm and runtime.load_gm. + q = p; + p = appendp(p); + p->as = AMOVW; + p->scond = 14; + p->reg = NREG; + if(flag_shared) { + p->from.type = D_OREG; + p->from.offset = 0; + } else { + p->from.type = D_CONST; + p->from.offset = tlsoffset; + } + p->from.sym = gmsym; + p->from.name = D_EXTERN; + p->to.type = D_REG; + p->to.reg = REGTMP; + p->to.offset = 0; + + p = appendp(p); + p->as = AADD; + p->scond = 14; + p->reg = NREG; + p->from.type = D_REG; + p->from.reg = REGTMP; + p->to.type = D_REG; + p->to.reg = (q->to.offset & 0xf000) >> 12; + p->to.offset = 0; + + if(flag_shared) { + p = appendp(p); + p->as = ASUB; + p->scond = 14; + p->reg = NREG; + p->from.type = D_CONST; + p->from.offset = -tlsoffset; + p->to.type = D_REG; + p->to.reg = (q->to.offset & 0xf000) >> 12; + p->to.offset = 0; + } + } + } } q = p; } @@ -180,160 +258,47 @@ noops(void) break; } - if(p->reg & NOSPLIT) { - q1 = prg(); - q1->as = AMOVW; - q1->scond |= C_WBIT; - q1->line = p->line; - q1->from.type = D_REG; - q1->from.reg = REGLINK; - q1->to.type = D_OREG; - q1->to.offset = -autosize; - q1->to.reg = REGSP; - q1->spadj = autosize; - q1->link = p->link; - p->link = q1; - } else if (autosize < StackBig) { - // split stack check for small functions - // MOVW g_stackguard(g), R1 - // CMP R1, $-autosize(SP) - // MOVW.LO $autosize, R1 - // MOVW.LO $args, R2 - // MOVW.LO R14, R3 - // BL.LO runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // TODO(kaib): add more trampolines - // TODO(kaib): put stackguard in register - // TODO(kaib): add support for -K and underflow detection - - // MOVW g_stackguard(g), R1 + if(!(p->reg & NOSPLIT)) + p = stacksplit(p, autosize); // emit split check + + // MOVW.W R14,$-autosize(SP) + p = appendp(p); + p->as = AMOVW; + p->scond |= C_WBIT; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_OREG; + p->to.offset = -autosize; + p->to.reg = REGSP; + p->spadj = autosize; + + if(cursym->text->reg & WRAPPER) { + // g->panicwrap += autosize; + // MOVW panicwrap_offset(g), R3 + // ADD $autosize, R3 + // MOVW R3 panicwrap_offset(g) p = appendp(p); p->as = AMOVW; p->from.type = D_OREG; p->from.reg = REGG; - p->to.type = D_REG; - p->to.reg = 1; - - if(autosize < StackSmall) { - // CMP R1, SP - p = appendp(p); - p->as = ACMP; - p->from.type = D_REG; - p->from.reg = 1; - p->reg = REGSP; - } else { - // MOVW $-autosize(SP), R2 - // CMP R1, R2 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.reg = REGSP; - p->from.offset = -autosize; - p->to.type = D_REG; - p->to.reg = 2; - - p = appendp(p); - p->as = ACMP; - p->from.type = D_REG; - p->from.reg = 1; - p->reg = 2; - } - - // MOVW.LO $autosize, R1 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = autosize; - p->to.type = D_REG; - p->to.reg = 1; - - // MOVW.LO $args, R2 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = (cursym->text->to.offset2 + 3) & ~3; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW.LO R14, R3 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_REG; - p->from.reg = REGLINK; + p->from.offset = 2*PtrSize; p->to.type = D_REG; p->to.reg = 3; - - // BL.LO runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->scond = C_SCOND_LO; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) - p = appendp(p); - p->as = AMOVW; - p->scond |= C_WBIT; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - p->spadj = autosize; - } else { // > StackBig - // MOVW $autosize, R1 - // MOVW $args, R2 - // MOVW R14, R3 - // BL runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // MOVW $autosize, R1 + p = appendp(p); - p->as = AMOVW; + p->as = AADD; p->from.type = D_CONST; p->from.offset = autosize; p->to.type = D_REG; - p->to.reg = 1; - - // MOVW $args, R2 - // also need to store the extra 4 bytes. - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.offset = (cursym->text->to.offset2 + 3) & ~3; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW R14, R3 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_REG; p->to.reg = 3; - - // BL runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) + p = appendp(p); p->as = AMOVW; - p->scond |= C_WBIT; p->from.type = D_REG; - p->from.reg = REGLINK; + p->from.reg = 3; p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - p->spadj = autosize; + p->to.reg = REGG; + p->to.offset = 2*PtrSize; } break; @@ -343,12 +308,56 @@ noops(void) if(!autosize) { p->as = AB; p->from = zprg.from; - p->to.type = D_OREG; - p->to.offset = 0; - p->to.reg = REGLINK; + if(p->to.sym) { // retjmp + p->to.type = D_BRANCH; + p->cond = p->to.sym->text; + } else { + p->to.type = D_OREG; + p->to.offset = 0; + p->to.reg = REGLINK; + } break; } } + + if(cursym->text->reg & WRAPPER) { + int cond; + + // Preserve original RET's cond, to allow RET.EQ + // in the implementation of reflect.call. + cond = p->scond; + p->scond = C_SCOND_NONE; + + // g->panicwrap -= autosize; + // MOVW panicwrap_offset(g), R3 + // SUB $autosize, R3 + // MOVW R3 panicwrap_offset(g) + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->from.offset = 2*PtrSize; + p->to.type = D_REG; + p->to.reg = 3; + p = appendp(p); + + p->as = ASUB; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = 3; + p = appendp(p); + + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = 3; + p->to.type = D_OREG; + p->to.reg = REGG; + p->to.offset = 2*PtrSize; + p = appendp(p); + + p->scond = cond; + } + p->as = AMOVW; p->scond |= C_PBIT; p->from.type = D_OREG; @@ -359,6 +368,17 @@ noops(void) // If there are instructions following // this ARET, they come from a branch // with the same stackframe, so no spadj. + + if(p->to.sym) { // retjmp + p->to.reg = REGLINK; + q2 = appendp(p); + q2->as = AB; + q2->to.type = D_BRANCH; + q2->to.sym = p->to.sym; + q2->cond = p->to.sym->text; + p->to.sym = nil; + p = q2; + } break; case AADD: @@ -452,14 +472,27 @@ noops(void) p->to.reg = REGSP; p->spadj = -8; - /* SUB $8,SP */ - q1->as = ASUB; - q1->from.type = D_CONST; - q1->from.offset = 8; - q1->from.reg = NREG; + /* Keep saved LR at 0(SP) after SP change. */ + /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */ + /* TODO: Remove SP adjustments; see issue 6699. */ + q1->as = AMOVW; + q1->from.type = D_OREG; + q1->from.reg = REGSP; + q1->from.offset = 0; q1->reg = NREG; q1->to.type = D_REG; + q1->to.reg = REGTMP; + + /* SUB $8,SP */ + q1 = appendp(q1); + q1->as = AMOVW; + q1->from.type = D_REG; + q1->from.reg = REGTMP; + q1->reg = NREG; + q1->to.type = D_OREG; q1->to.reg = REGSP; + q1->to.offset = -8; + q1->scond |= C_WBIT; q1->spadj = 8; break; @@ -476,6 +509,143 @@ noops(void) } } +static Prog* +stacksplit(Prog *p, int32 framesize) +{ + int32 arg; + + // MOVW g_stackguard(g), R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->to.type = D_REG; + p->to.reg = 1; + + if(framesize <= StackSmall) { + // small stack: SP < stackguard + // CMP stackguard, SP + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = REGSP; + } else if(framesize <= StackBig) { + // large stack: SP-framesize < stackguard-StackSmall + // MOVW $-framesize(SP), R2 + // CMP stackguard, R2 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = -framesize; + p->to.type = D_REG; + p->to.reg = 2; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + } else { + // Such a large stack we need to protect against wraparound + // if SP is close to zero. + // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) + // The +StackGuard on both sides is required to keep the left side positive: + // SP is allowed to be slightly below stackguard. See stack.h. + // CMP $StackPreempt, R1 + // MOVW.NE $StackGuard(SP), R2 + // SUB.NE R1, R2 + // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 + // CMP.NE R3, R2 + p = appendp(p); + p->as = ACMP; + p->from.type = D_CONST; + p->from.offset = (uint32)StackPreempt; + p->reg = 1; + + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = StackGuard; + p->to.type = D_REG; + p->to.reg = 2; + p->scond = C_SCOND_NE; + + p = appendp(p); + p->as = ASUB; + p->from.type = D_REG; + p->from.reg = 1; + p->to.type = D_REG; + p->to.reg = 2; + p->scond = C_SCOND_NE; + + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = framesize + (StackGuard - StackSmall); + p->to.type = D_REG; + p->to.reg = 3; + p->scond = C_SCOND_NE; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 3; + p->reg = 2; + p->scond = C_SCOND_NE; + } + + // MOVW.LS $framesize, R1 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_CONST; + p->from.offset = framesize; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW.LS $args, R2 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_CONST; + arg = cursym->text->to.offset2; + if(arg == 1) // special marker for known 0 + arg = 0; + if(arg&3) + diag("misaligned argument size in stack split"); + p->from.offset = arg; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW.LS R14, R3 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = 3; + + // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted + p = appendp(p); + p->as = ABL; + p->scond = C_SCOND_LS; + p->to.type = D_BRANCH; + p->to.sym = symmorestack; + p->cond = pmorestack; + + // BLS start + p = appendp(p); + p->as = ABLS; + p->to.type = D_BRANCH; + p->cond = cursym->text->link; + + return p; +} + static void sigdiv(char *n) { |