blob: 4a7f66af31ae76a6249d25fd14db5a08426a0cc6 [file] [log] [blame]
// 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; i<NHASH; i++)
for(s = hash[i]; s != S; s = s->link) {
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; i<NHASH; i++)
for(s = hash[i]; s != S; s = s->link) {
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; i<NHASH; i++)
for(s = hash[i]; s != S; s = s->link) {
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; i<NHASH; i++)
for(s = hash[i]; s != S; s = s->link) {
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 : "<nil>");
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; i<LOG; i++) {
if(i == 0)
cnt[i] = 1; else
cnt[i] = LOG * cnt[i-1];
dwn[i] = 1;
lst[i] = P;
}
i = 0;
for(p = firstp; p != P; p = p->link) {
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; i<nelem(morename); i++) {
symmorestack[i] = lookup(morename[i], 0);
pmorestack[i] = P;
}
for(p = firstp; p != P; p = p->link) {
if(p->as == ATEXT) {
for(i=0; i<nelem(morename); i++) {
if(p->from.sym == symmorestack[i]) {
pmorestack[i] = p;
break;
}
}
}
}
for(i=0; i<nelem(morename); i++) {
if(pmorestack[i] == P)
diag("morestack trampoline not defined");
}
curframe = 0;
curbecome = 0;
maxbecome = 0;
curtext = 0;
for(p = firstp; p != P; p = p->link) {
/* 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; i<NHASH; i++)
for(s = hash[i]; s != S; s = s->link)
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<<Roffset)
diag("relocation offset %ld for %s out of range", v, s->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);
}