// Inferno utils/6l/pass.c // http://code.google.com/p/inferno-os/source/browse/utils/6l/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. #include "l.h" #include "../ld/lib.h" // see ../../runtime/proc.c:/StackGuard enum { StackSmall = 128, StackBig = 4096, }; void dodata(void) { int i; Sym *s; Prog *p; int32 t, u; if(debug['v']) Bprint(&bso, "%5.2f dodata\n", cputime()); Bflush(&bso); for(p = datap; p != P; p = p->link) { curtext = p; // for diag messages s = p->from.sym; if(p->as == ADYNT || p->as == AINIT) s->value = dtype; if(s->type == SBSS) s->type = SDATA; if(s->type != SDATA) diag("initialize non-data (%d): %s\n%P", s->type, s->name, p); t = p->from.offset + p->width; if(t > s->value) diag("initialize bounds (%lld): %s\n%P", s->value, s->name, p); } /* allocate small guys */ datsize = 0; for(i=0; ilink) { if(!s->reachable) continue; if(s->type != SDATA) if(s->type != SBSS) continue; t = s->value; if(t == 0 && s->name[0] != '.') { diag("%s: no size", s->name); t = 1; } t = rnd(t, 4); s->value = t; if(t > MINSIZ) continue; if(t >= 8) datsize = rnd(datsize, 8); s->size = t; s->value = datsize; datsize += t; s->type = SDATA1; } /* allocate the rest of the data */ for(i=0; ilink) { if(!s->reachable) continue; if(s->type != SDATA) { if(s->type == SDATA1) s->type = SDATA; continue; } t = s->value; if(t >= 8) datsize = rnd(datsize, 8); s->size = t; s->value = datsize; datsize += t; } if(datsize) datsize = rnd(datsize, 8); if(debug['j']) { /* * pad data with bss that fits up to next * 8k boundary, then push data to 8k */ u = rnd(datsize, 8192); u -= datsize; for(i=0; ilink) { if(!s->reachable) continue; if(s->type != SBSS) continue; t = s->value; if(t > u) continue; u -= t; s->size = t; s->value = datsize; s->type = SDATA; datsize += t; } datsize += u; } } void dobss(void) { int i; Sym *s; int32 t; if(dynptrsize > 0) { /* dynamic pointer section between data and bss */ datsize = rnd(datsize, 8); } /* now the bss */ bsssize = 0; for(i=0; ilink) { if(!s->reachable) continue; if(s->type != SBSS) continue; t = s->value; s->size = t; if(t >= 8) bsssize = rnd(bsssize, 8); s->value = bsssize + dynptrsize + datsize; bsssize += t; } xdefine("data", SBSS, 0); xdefine("edata", SBSS, datsize); xdefine("end", SBSS, dynptrsize + bsssize + datsize); } 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) { if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); firstp = prg(); lastp = firstp; xfol(textp); lastp->link = P; firstp = firstp->link; } void xfol(Prog *p) { Prog *q; int i; enum as a; loop: if(p == P) return; if(p->as == ATEXT) curtext = p; if(!curtext->from.sym->reachable) { p = p->pcond; goto loop; } if(p->as == AJMP) if((q = p->pcond) != P && q->as != ATEXT) { p->mark = 1; p = q; if(p->mark == 0) goto loop; } if(p->mark) { /* copy up to 4 instructions to avoid branch */ for(i=0,q=p; i<4; i++,q=q->link) { if(q == P) break; if(q == lastp) break; a = q->as; if(a == ANOP) { i--; continue; } switch(a) { case AJMP: case ARET: case AIRETL: case AIRETQ: case AIRETW: case ARETFL: case ARETFQ: case ARETFW: case APUSHL: case APUSHFL: case APUSHQ: case APUSHFQ: case APUSHW: case APUSHFW: case APOPL: case APOPFL: case APOPQ: case APOPFQ: case APOPW: case APOPFW: goto brk; } 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; lastp->link = q; lastp = 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); p = q->link; if(p->mark) return; goto loop; } } /* */ brk:; q = prg(); q->as = AJMP; q->line = p->line; q->to.type = D_BRANCH; q->to.offset = p->pc; q->pcond = p; p = q; } p->mark = 1; lastp->link = p; lastp = p; a = p->as; if(a == AJMP || a == ARET || a == AIRETL || a == AIRETQ || a == AIRETW || a == ARETFL || a == ARETFQ || a == ARETFW) return; if(p->pcond != P) if(a != ACALL) { 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); q = brchain(p->pcond); if(q->mark) { p->pcond = q; return; } p = q; goto loop; } p = p->link; goto loop; } Prog* byteq(int v) { Prog *p; p = prg(); p->as = ABYTE; p->from.type = D_CONST; p->from.offset = v&0xff; return p; } 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 doinit(void) { Sym *s; Prog *p; int x; for(p = datap; p != P; p = p->link) { x = p->to.type; if(x != D_EXTERN && x != D_STATIC) continue; s = p->to.sym; if(s->type == 0 || s->type == SXREF) diag("undefined %s initializer of %s", s->name, p->from.sym->name); p->to.offset += s->value; p->to.type = D_CONST; if(s->type == SDATA || s->type == SBSS) p->to.offset += INITDAT; } } void patch(void) { int32 c; Prog *p, *q; Sym *s; int32 vexit; 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; for(p = firstp; p != P; p = p->link) { if(p->as == ATEXT) curtext = p; 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); switch(s->type) { default: /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; s->value = vexit; continue; // avoid more error messages case STEXT: p->to.offset = s->value; break; case SUNDEF: p->pcond = UP; p->to.offset = 0; break; } p->to.type = D_BRANCH; } } if(p->to.type != D_BRANCH || p->pcond == UP) continue; c = p->to.offset; for(q = firstp; q != P;) { if(q->forwd != P) if(c >= q->forwd->pc) { q = q->forwd; continue; } if(c == q->pc) break; q = q->link; } if(q == P) { diag("branch out of range in %s\n%P [%s]", TNAME, p, p->to.sym ? p->to.sym->name : ""); p->to.type = D_NONE; } p->pcond = q; } for(p = firstp; p != P; p = p->link) { if(p->as == ATEXT) curtext = p; p->mark = 0; /* initialization for follow */ if(p->pcond != P && p->pcond != UP) { p->pcond = brloop(p->pcond); if(p->pcond != P) if(p->to.type == D_BRANCH) p->to.offset = p->pcond->pc; } } } #define LOG 5 void mkfwd(void) { Prog *p; int i; int32 dwn[LOG], cnt[LOG]; Prog *lst[LOG]; for(i=0; ilink) { if(p->as == ATEXT) curtext = p; i--; if(i < 0) i = LOG-1; p->forwd = P; dwn[i]--; if(dwn[i] <= 0) { dwn[i] = cnt[i]; if(lst[i] != P) lst[i]->forwd = p; lst[i] = p; } } } 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; } static char* morename[] = { "runtime·morestack00", "runtime·morestack10", "runtime·morestack01", "runtime·morestack11", "runtime·morestack8", "runtime·morestack16", "runtime·morestack24", "runtime·morestack32", "runtime·morestack40", "runtime·morestack48", }; Prog* pmorestack[nelem(morename)]; Sym* symmorestack[nelem(morename)]; void dostkoff(void) { Prog *p, *q, *q1; int32 autoffset, deltasp; int a, f, curframe, curbecome, maxbecome, pcsize; uint32 moreconst1, moreconst2, i; for(i=0; ilink) { if(p->as == ATEXT) { for(i=0; ifrom.sym == symmorestack[i]) { pmorestack[i] = p; break; } } } } for(i=0; ilink) { /* find out how much arg space is used in this TEXT */ if(p->to.type == (D_INDIR+D_SP)) if(p->to.offset > curframe) curframe = p->to.offset; switch(p->as) { case ATEXT: if(curtext && curtext->from.sym) { curtext->from.sym->frame = curframe; curtext->from.sym->become = curbecome; if(curbecome > maxbecome) maxbecome = curbecome; } curframe = 0; curbecome = 0; curtext = p; break; case ARET: /* special form of RET is BECOME */ if(p->from.type == D_CONST) if(p->from.offset > curbecome) curbecome = p->from.offset; break; } } if(curtext && curtext->from.sym) { curtext->from.sym->frame = curframe; curtext->from.sym->become = curbecome; if(curbecome > maxbecome) maxbecome = curbecome; } if(debug['b']) print("max become = %d\n", maxbecome); xdefine("ALEFbecome", STEXT, maxbecome); curtext = 0; for(p = firstp; p != P; p = p->link) { switch(p->as) { case ATEXT: curtext = p; break; case ACALL: if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { f = maxbecome - curtext->from.sym->frame; if(f <= 0) break; /* calling a become or calling a variable */ if(p->to.sym == S || p->to.sym->become) { curtext->to.offset += f; if(debug['b']) { curp = p; print("%D calling %D increase %d\n", &curtext->from, &p->to, f); } } } break; } } autoffset = 0; deltasp = 0; for(p = firstp; p != P; p = p->link) { if(p->as == ATEXT) { curtext = p; parsetextconst(p->to.offset); autoffset = textstksiz; if(autoffset < 0) autoffset = 0; q = P; q1 = P; if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) diag("nosplit func likely to overflow stack"); if(!(p->from.scale & NOSPLIT)) { if(debug['K']) { // 6l -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 = ACMPQ; p->from.type = D_INDIR+D_R15; p->from.offset = 8; p->to.type = D_SP; p = appendp(p); p->as = AJHI; 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; } if(autoffset < StackBig) { // do we need to call morestack? if(autoffset <= StackSmall) { // small stack p = appendp(p); p->as = ACMPQ; p->from.type = D_SP; p->to.type = D_INDIR+D_R15; if(q1) { q1->pcond = p; q1 = P; } } else { // large stack p = appendp(p); p->as = ALEAQ; p->from.type = D_INDIR+D_SP; p->from.offset = -(autoffset-StackSmall); p->to.type = D_AX; if(q1) { q1->pcond = p; q1 = P; } p = appendp(p); p->as = ACMPQ; p->from.type = D_AX; p->to.type = D_INDIR+D_R15; } // common p = appendp(p); p->as = AJHI; p->to.type = D_BRANCH; p->to.offset = 4; q = p; } /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ moreconst1 = 0; if(autoffset+160 > 4096) moreconst1 = (autoffset+160) & ~7LL; moreconst2 = textarg; // 4 varieties varieties (const1==0 cross const2==0) // and 6 subvarieties of (const1==0 and const2!=0) p = appendp(p); if(moreconst1 == 0 && moreconst2 == 0) { p->as = ACALL; p->to.type = D_BRANCH; p->pcond = pmorestack[0]; p->to.sym = symmorestack[0]; if(q1) { q1->pcond = p; q1 = P; } } else if(moreconst1 != 0 && moreconst2 == 0) { p->as = AMOVL; p->from.type = D_CONST; p->from.offset = moreconst1; p->to.type = D_AX; if(q1) { q1->pcond = p; q1 = P; } p = appendp(p); p->as = ACALL; p->to.type = D_BRANCH; p->pcond = pmorestack[1]; p->to.sym = symmorestack[1]; } else if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { i = moreconst2/8 + 3; p->as = ACALL; p->to.type = D_BRANCH; p->pcond = pmorestack[i]; p->to.sym = symmorestack[i]; if(q1) { q1->pcond = p; q1 = P; } } else if(moreconst1 == 0 && moreconst2 != 0) { p->as = AMOVL; p->from.type = D_CONST; p->from.offset = moreconst2; p->to.type = D_AX; if(q1) { q1->pcond = p; q1 = P; } p = appendp(p); p->as = ACALL; p->to.type = D_BRANCH; p->pcond = pmorestack[2]; p->to.sym = symmorestack[2]; } else { p->as = AMOVQ; p->from.type = D_CONST; p->from.offset = (uint64)moreconst2 << 32; p->from.offset |= moreconst1; p->to.type = D_AX; if(q1) { q1->pcond = p; q1 = P; } p = appendp(p); p->as = ACALL; p->to.type = D_BRANCH; p->pcond = pmorestack[3]; p->to.sym = symmorestack[3]; } } if(q != P) q->pcond = p->link; if(autoffset) { p = appendp(p); p->as = AADJSP; p->from.type = D_CONST; p->from.offset = autoffset; if(q != P) q->pcond = p; } deltasp = autoffset; if(debug['K'] > 1 && autoffset) { // 6l -KK means double-check for stack overflow // even after calling morestack and even if the // function is marked as nosplit. p = appendp(p); p->as = AMOVQ; p->from.type = D_INDIR+D_R15; p->from.offset = 0; p->to.type = D_BX; p = appendp(p); p->as = ASUBQ; p->from.type = D_CONST; p->from.offset = StackSmall+32; p->to.type = D_BX; p = appendp(p); p->as = ACMPQ; p->from.type = D_SP; p->to.type = D_BX; p = appendp(p); p->as = AJHI; p->to.type = D_BRANCH; 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; q1 = P; } } pcsize = p->mode/8; a = p->from.type; if(a == D_AUTO) p->from.offset += deltasp; if(a == D_PARAM) p->from.offset += deltasp + pcsize; a = p->to.type; if(a == D_AUTO) p->to.offset += deltasp; if(a == D_PARAM) p->to.offset += deltasp + pcsize; switch(p->as) { default: continue; case APUSHL: case APUSHFL: deltasp += 4; continue; case APUSHQ: case APUSHFQ: deltasp += 8; continue; case APUSHW: case APUSHFW: deltasp += 2; continue; case APOPL: case APOPFL: deltasp -= 4; continue; case APOPQ: case APOPFQ: deltasp -= 8; continue; case APOPW: case APOPFW: deltasp -= 2; continue; case ARET: break; } if(autoffset != deltasp) diag("unbalanced PUSH/POP"); if(p->from.type == D_CONST) goto become; if(autoffset) { p->as = AADJSP; p->from.type = D_CONST; p->from.offset = -autoffset; p = appendp(p); p->as = ARET; } continue; become: q = p; p = appendp(p); p->as = AJMP; p->to = q->to; p->pcond = q->pcond; q->as = AADJSP; q->from = zprg.from; q->from.type = D_CONST; q->from.offset = -autoffset; q->to = zprg.to; continue; } } vlong atolwhex(char *s) { vlong 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; } void undef(void) { int i; Sym *s; for(i=0; ilink) if(s->type == SXREF) diag("%s: not defined", s->name); } void import(void) { int i; Sym *s; for(i = 0; i < NHASH; i++) for(s = hash[i]; s != S; s = s->link) if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ if(s->value != 0) diag("value != 0 on SXREF"); undefsym(s); Bprint(&bso, "IMPORT: %s sig=%lux v=%lld\n", s->name, s->sig, s->value); if(debug['S']) s->sig = 0; } } void ckoff(Sym *s, int32 v) { if(v < 0 || v >= 1<name); } Prog* newdata(Sym *s, int o, int w, int t) { Prog *p; p = prg(); if(edatap == P) datap = p; else edatap->link = p; edatap = p; p->as = ADATA; p->width = w; p->from.scale = w; p->from.type = t; p->from.sym = s; p->from.offset = o; p->to.type = D_CONST; p->dlink = s->data; s->data = p; return p; } Prog* newtext(Prog *p, Sym *s) { if(p == P) { p = prg(); p->as = ATEXT; p->from.sym = s; } s->type = STEXT; s->text = p; s->value = pc; lastp->link = p; lastp = p; p->pc = pc++; if(textp == P) textp = p; else etextp->pcond = p; etextp = p; return p; } void export(void) { int i, j, n, off, nb, sv, ne; Sym *s, *et, *str, **esyms; Prog *p; char buf[NSNAME], *t; n = 0; for(i = 0; i < NHASH; i++) for(s = hash[i]; s != S; s = s->link) if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) n++; esyms = mal(n*sizeof(Sym*)); ne = n; n = 0; for(i = 0; i < NHASH; i++) for(s = hash[i]; s != S; s = s->link) if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) esyms[n++] = s; for(i = 0; i < ne-1; i++) for(j = i+1; j < ne; j++) if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ s = esyms[i]; esyms[i] = esyms[j]; esyms[j] = s; } nb = 0; off = 0; et = lookup(EXPTAB, 0); if(et->type != 0 && et->type != SXREF) diag("%s already defined", EXPTAB); et->type = SDATA; str = lookup(".string", 0); if(str->type == 0) str->type = SDATA; sv = str->value; for(i = 0; i < ne; i++){ s = esyms[i]; if(debug['S']) s->sig = 0; /* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */ /* signature */ p = newdata(et, off, sizeof(int32), D_EXTERN); off += sizeof(int32); p->to.offset = s->sig; /* address */ p = newdata(et, off, sizeof(int32), D_EXTERN); off += sizeof(int32); p->to.type = D_ADDR; p->to.index = D_EXTERN; p->to.sym = s; /* string */ t = s->name; n = strlen(t)+1; for(;;){ buf[nb++] = *t; sv++; if(nb >= NSNAME){ p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); p->to.type = D_SCONST; memmove(p->to.scon, buf, NSNAME); nb = 0; } if(*t++ == 0) break; } /* name */ p = newdata(et, off, sizeof(int32), D_EXTERN); off += sizeof(int32); p->to.type = D_ADDR; p->to.index = D_STATIC; p->to.sym = str; p->to.offset = sv-n; } if(nb > 0){ p = newdata(str, sv-nb, nb, D_STATIC); p->to.type = D_SCONST; memmove(p->to.scon, buf, nb); } for(i = 0; i < 3; i++){ newdata(et, off, sizeof(int32), D_EXTERN); off += sizeof(int32); } et->value = off; if(sv == 0) sv = 1; str->value = sv; exports = ne; free(esyms); }