// Inferno utils/8l/pass.c // http://code.google.com/p/inferno-os/source/browse/utils/8l/pass.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code and data passes. #include "l.h" #include "../ld/lib.h" static void xfol(Prog*, Prog**); // see ../../pkg/runtime/proc.c:/StackGuard enum { #ifdef __WINDOWS__ // use larger stacks to compensate for larger stack guard, // needed for exception handling. StackSmall = 256, StackBig = 8192, #else StackSmall = 128, StackBig = 4096, #endif }; Prog* brchain(Prog *p) { int i; for(i=0; i<20; i++) { if(p == P || p->as != AJMP) return p; p = p->pcond; } return P; } void follow(void) { Prog *firstp, *lastp; if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); for(cursym = textp; cursym != nil; cursym = cursym->next) { firstp = prg(); lastp = firstp; xfol(cursym->text, &lastp); lastp->link = nil; cursym->text = firstp->link; } } static int nofollow(int a) { switch(a) { case AJMP: case ARET: case AIRETL: case AIRETW: return 1; } return 0; } static int pushpop(int a) { switch(a) { case APUSHL: case APUSHFL: case APUSHW: case APUSHFW: case APOPL: case APOPFL: case APOPW: case APOPFW: return 1; } return 0; } static void xfol(Prog *p, Prog **last) { Prog *q; int i; enum as a; loop: if(p == P) return; if(p->as == AJMP) if((q = p->pcond) != P && q->as != ATEXT) { /* mark instruction as done and continue layout at target of jump */ p->mark = 1; p = q; if(p->mark == 0) goto loop; } if(p->mark) { /* * p goes here, but already used it elsewhere. * copy up to 4 instructions or else branch to other copy. */ for(i=0,q=p; i<4; i++,q=q->link) { if(q == P) break; if(q == *last) break; a = q->as; if(a == ANOP) { i--; continue; } if(nofollow(a) || pushpop(a)) break; // NOTE(rsc): arm does goto copy if(q->pcond == P || q->pcond->mark) continue; if(a == ACALL || a == ALOOP) continue; for(;;) { if(p->as == ANOP) { p = p->link; continue; } q = copyp(p); p = p->link; q->mark = 1; (*last)->link = q; *last = q; if(q->as != a || q->pcond == P || q->pcond->mark) continue; q->as = relinv(q->as); p = q->pcond; q->pcond = q->link; q->link = p; xfol(q->link, last); p = q->link; if(p->mark) return; goto loop; } } /* */ q = prg(); q->as = AJMP; q->line = p->line; q->to.type = D_BRANCH; q->to.offset = p->pc; q->pcond = p; p = q; } /* emit p */ p->mark = 1; (*last)->link = p; *last = p; a = p->as; /* continue loop with what comes after p */ if(nofollow(a)) return; if(p->pcond != P && a != ACALL) { /* * some kind of conditional branch. * recurse to follow one path. * continue loop on the other. */ q = brchain(p->link); if(q != P && q->mark) if(a != ALOOP) { p->as = relinv(a); p->link = p->pcond; p->pcond = q; } xfol(p->link, last); q = brchain(p->pcond); if(q->mark) { p->pcond = q; return; } p = q; goto loop; } p = p->link; goto loop; } int relinv(int a) { switch(a) { case AJEQ: return AJNE; case AJNE: return AJEQ; case AJLE: return AJGT; case AJLS: return AJHI; case AJLT: return AJGE; case AJMI: return AJPL; case AJGE: return AJLT; case AJPL: return AJMI; case AJGT: return AJLE; case AJHI: return AJLS; case AJCS: return AJCC; case AJCC: return AJCS; case AJPS: return AJPC; case AJPC: return AJPS; case AJOS: return AJOC; case AJOC: return AJOS; } diag("unknown relation: %s in %s", anames[a], TNAME); return a; } void patch(void) { int32 c; Prog *p, *q; Sym *s; int32 vexit; Sym *plan9_tos; if(debug['v']) Bprint(&bso, "%5.2f mkfwd\n", cputime()); Bflush(&bso); mkfwd(); if(debug['v']) Bprint(&bso, "%5.2f patch\n", cputime()); Bflush(&bso); s = lookup("exit", 0); vexit = s->value; plan9_tos = S; if(HEADTYPE == Hplan9x32) plan9_tos = lookup("_tos", 0); for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { if(HEADTYPE == Hwindows) { // Convert // op n(GS), reg // to // MOVL 0x2C(FS), reg // op n(reg), reg // The purpose of this patch is to fix some accesses // to extern register variables (TLS) on Windows, as // a different method is used to access them. if(p->from.type == D_INDIR+D_GS && p->to.type >= D_AX && p->to.type <= D_DI) { q = appendp(p); q->from = p->from; q->from.type = D_INDIR + p->to.type; q->to = p->to; q->as = p->as; p->as = AMOVL; p->from.type = D_INDIR+D_FS; p->from.offset = 0x2C; } } if(HEADTYPE == Hlinux) { // Running binaries under Xen requires using // MOVL 0(GS), reg // and then off(reg) instead of saying off(GS) directly // when the offset is negative. if(p->from.type == D_INDIR+D_GS && p->from.offset < 0 && p->to.type >= D_AX && p->to.type <= D_DI) { q = appendp(p); q->from = p->from; q->from.type = D_INDIR + p->to.type; q->to = p->to; q->as = p->as; p->as = AMOVL; p->from.type = D_INDIR+D_GS; p->from.offset = 0; } } if(HEADTYPE == Hplan9x32) { if(p->from.type == D_INDIR+D_GS && p->to.type >= D_AX && p->to.type <= D_DI) { q = appendp(p); q->from = p->from; q->from.type = D_INDIR + p->to.type; q->to = p->to; q->as = p->as; p->as = AMOVL; p->from.type = D_EXTERN; p->from.sym = plan9_tos; p->from.offset = 0; } } if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { s = p->to.sym; if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); if((s->type&~SSUB) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; s->value = vexit; continue; // avoid more error messages } if(s->text == nil) continue; p->to.type = D_BRANCH; p->to.offset = s->text->pc; p->pcond = s->text; continue; } } if(p->to.type != D_BRANCH) continue; c = p->to.offset; for(q = cursym->text; q != P;) { if(c == q->pc) break; if(q->forwd != P && c >= q->forwd->pc) q = q->forwd; else q = q->link; } if(q == P) { diag("branch out of range in %s (%#ux)\n%P [%s]", TNAME, c, p, p->to.sym ? p->to.sym->name : ""); p->to.type = D_NONE; } p->pcond = q; } } for(cursym = textp; cursym != nil; cursym = cursym->next) { if(cursym->text == nil || cursym->p != nil) continue; for(p = cursym->text; p != P; p = p->link) { p->mark = 0; /* initialization for follow */ if(p->pcond != P) { p->pcond = brloop(p->pcond); if(p->pcond != P) if(p->to.type == D_BRANCH) p->to.offset = p->pcond->pc; } } } } Prog* brloop(Prog *p) { int c; Prog *q; c = 0; for(q = p; q != P; q = q->pcond) { if(q->as != AJMP) break; c++; if(c >= 5000) return P; } return q; } void dostkoff(void) { Prog *p, *q, *q1; int32 autoffset, deltasp; int a; Prog *pmorestack; Sym *symmorestack; Sym *plan9_tos; pmorestack = P; symmorestack = lookup("runtime.morestack", 0); if(symmorestack->type != STEXT) diag("runtime.morestack not defined"); else { pmorestack = symmorestack->text; symmorestack->text->from.scale |= NOSPLIT; } plan9_tos = S; if(HEADTYPE == Hplan9x32) plan9_tos = lookup("_tos", 0); for(cursym = textp; cursym != nil; cursym = cursym->next) { if(cursym->text == nil || cursym->text->link == nil) continue; p = cursym->text; autoffset = p->to.offset; if(autoffset < 0) autoffset = 0; q = P; q1 = P; if(pmorestack != P) if(!(p->from.scale & NOSPLIT)) { p = appendp(p); // load g into CX switch(HEADTYPE) { case Hwindows: p->as = AMOVL; p->from.type = D_INDIR+D_FS; p->from.offset = 0x2c; p->to.type = D_CX; p = appendp(p); p->as = AMOVL; p->from.type = D_INDIR+D_CX; p->from.offset = 0; p->to.type = D_CX; break; case Hlinux: p->as = AMOVL; p->from.type = D_INDIR+D_GS; p->from.offset = 0; p->to.type = D_CX; p = appendp(p); p->as = AMOVL; p->from.type = D_INDIR+D_CX; p->from.offset = tlsoffset + 0; p->to.type = D_CX; break; case Hplan9x32: p->as = AMOVL; p->from.type = D_EXTERN; p->from.sym = plan9_tos; p->to.type = D_CX; p = appendp(p); p->as = AMOVL; p->from.type = D_INDIR+D_CX; p->from.offset = tlsoffset + 0; p->to.type = D_CX; break; default: p->as = AMOVL; p->from.type = D_INDIR+D_GS; p->from.offset = tlsoffset + 0; p->to.type = D_CX; } if(debug['K']) { // 8l -K means check not only for stack // overflow but stack underflow. // On underflow, INT 3 (breakpoint). // Underflow itself is rare but this also // catches out-of-sync stack guard info. p = appendp(p); p->as = ACMPL; p->from.type = D_INDIR+D_CX; p->from.offset = 4; p->to.type = D_SP; p = appendp(p); p->as = AJCC; p->to.type = D_BRANCH; p->to.offset = 4; q1 = p; p = appendp(p); p->as = AINT; p->from.type = D_CONST; p->from.offset = 3; p = appendp(p); p->as = ANOP; q1->pcond = p; } if(autoffset < StackBig) { // do we need to call morestack if(autoffset <= StackSmall) { // small stack p = appendp(p); p->as = ACMPL; p->from.type = D_SP; p->to.type = D_INDIR+D_CX; } else { // large stack p = appendp(p); p->as = ALEAL; p->from.type = D_INDIR+D_SP; p->from.offset = -(autoffset-StackSmall); p->to.type = D_AX; p = appendp(p); p->as = ACMPL; p->from.type = D_AX; p->to.type = D_INDIR+D_CX; } // common p = appendp(p); p->as = AJHI; p->to.type = D_BRANCH; p->to.offset = 4; q = p; } p = appendp(p); // save frame size in DX p->as = AMOVL; p->to.type = D_DX; /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ p->from.type = D_CONST; if(autoffset+160+cursym->text->to.offset2 > 4096) p->from.offset = (autoffset+160) & ~7LL; p = appendp(p); // save arg size in AX p->as = AMOVL; p->to.type = D_AX; p->from.type = D_CONST; p->from.offset = cursym->text->to.offset2; p = appendp(p); p->as = ACALL; p->to.type = D_BRANCH; p->pcond = pmorestack; p->to.sym = symmorestack; } if(q != P) q->pcond = p->link; if(autoffset) { p = appendp(p); p->as = AADJSP; p->from.type = D_CONST; p->from.offset = autoffset; p->spadj = autoffset; if(q != P) q->pcond = p; } deltasp = autoffset; for(; p != P; p = p->link) { a = p->from.type; if(a == D_AUTO) p->from.offset += deltasp; if(a == D_PARAM) p->from.offset += deltasp + 4; a = p->to.type; if(a == D_AUTO) p->to.offset += deltasp; if(a == D_PARAM) p->to.offset += deltasp + 4; switch(p->as) { default: continue; case APUSHL: case APUSHFL: deltasp += 4; p->spadj = 4; continue; case APUSHW: case APUSHFW: deltasp += 2; p->spadj = 2; continue; case APOPL: case APOPFL: deltasp -= 4; p->spadj = -4; continue; case APOPW: case APOPFW: deltasp -= 2; p->spadj = -2; continue; case ARET: break; } if(autoffset != deltasp) diag("unbalanced PUSH/POP"); if(autoffset) { q = p; p = appendp(p); p->as = ARET; q->as = AADJSP; q->from.type = D_CONST; q->from.offset = -autoffset; p->spadj = -autoffset; } } } } int32 atolwhex(char *s) { int32 n; int f; n = 0; f = 0; while(*s == ' ' || *s == '\t') s++; if(*s == '-' || *s == '+') { if(*s++ == '-') f = 1; while(*s == ' ' || *s == '\t') s++; } if(s[0]=='0' && s[1]){ if(s[1]=='x' || s[1]=='X'){ s += 2; for(;;){ if(*s >= '0' && *s <= '9') n = n*16 + *s++ - '0'; else if(*s >= 'a' && *s <= 'f') n = n*16 + *s++ - 'a' + 10; else if(*s >= 'A' && *s <= 'F') n = n*16 + *s++ - 'A' + 10; else break; } } else while(*s >= '0' && *s <= '7') n = n*8 + *s++ - '0'; } else while(*s >= '0' && *s <= '9') n = n*10 + *s++ - '0'; if(f) n = -n; return n; }