This source file includes following definitions.
- datcmp
- listsort
- relocsym
- reloc
- dynrelocsym
- dynreloc
- blk
- codeblk
- datblk
- strnput
- addstrdata
- addstring
- dosymtype
- symalign
- aligndatsize
- maxalign
- gcaddsym
- growdatsize
- dodata
- textaddress
- address
#include        "l.h"
#include        "../ld/lib.h"
#include        "../ld/elf.h"
#include        "../ld/macho.h"
#include        "../ld/pe.h"
#include        "../../pkg/runtime/mgc0.h"
void    dynreloc(void);
int
datcmp(LSym *s1, LSym *s2)
{
        if(s1->type != s2->type)
                return (int)s1->type - (int)s2->type;
        if(s1->size != s2->size) {
                if(s1->size < s2->size)
                        return -1;
                return +1;
        }
        return strcmp(s1->name, s2->name);
}
LSym*
listsort(LSym *l, int (*cmp)(LSym*, LSym*), int off)
{
        LSym *l1, *l2, *le;
        #define NEXT(l) (*(LSym**)((char*)(l)+off))
        if(l == 0 || NEXT(l) == 0)
                return l;
        l1 = l;
        l2 = l;
        for(;;) {
                l2 = NEXT(l2);
                if(l2 == 0)
                        break;
                l2 = NEXT(l2);
                if(l2 == 0)
                        break;
                l1 = NEXT(l1);
        }
        l2 = NEXT(l1);
        NEXT(l1) = 0;
        l1 = listsort(l, cmp, off);
        l2 = listsort(l2, cmp, off);
        
        if(cmp(l1, l2) < 0) {
                l = l1;
                l1 = NEXT(l1);
        } else {
                l = l2;
                l2 = NEXT(l2);
        }
        le = l;
        for(;;) {
                if(l1 == 0) {
                        while(l2) {
                                NEXT(le) = l2;
                                le = l2;
                                l2 = NEXT(l2);
                        }
                        NEXT(le) = 0;
                        break;
                }
                if(l2 == 0) {
                        while(l1) {
                                NEXT(le) = l1;
                                le = l1;
                                l1 = NEXT(l1);
                        }
                        break;
                }
                if(cmp(l1, l2) < 0) {
                        NEXT(le) = l1;
                        le = l1;
                        l1 = NEXT(l1);
                } else {
                        NEXT(le) = l2;
                        le = l2;
                        l2 = NEXT(l2);
                }
        }
        NEXT(le) = 0;
        return l;
        
        #undef NEXT
}
void
relocsym(LSym *s)
{
        Reloc *r;
        LSym *rs;
        int32 i, off, siz, fl;
        vlong o;
        uchar *cast;
        ctxt->cursym = s;
        for(r=s->r; r<s->r+s->nr; r++) {
                r->done = 1;
                off = r->off;
                siz = r->siz;
                if(off < 0 || off+siz > s->np) {
                        diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np);
                        continue;
                }
                if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) {
                        diag("%s: not defined", r->sym->name);
                        continue;
                }
                if(r->type >= 256)
                        continue;
                if(r->siz == 0) 
                        continue;
                
                if(HEADTYPE != Hsolaris && r->sym != S && r->sym->type == SDYNIMPORT)
                        diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type);
                if(r->sym != S && r->sym->type != STLSBSS && !r->sym->reachable)
                        diag("unreachable sym in relocation: %s %s", s->name, r->sym->name);
                switch(r->type) {
                default:
                        o = 0;
                        if(archreloc(r, s, &o) < 0)
                                diag("unknown reloc %d", r->type);
                        break;
                case R_TLS:
                        if(linkmode == LinkInternal && iself && thechar == '5') {
                                
                                
                                
                                
                                
                                
                                
                                o = 8 + r->sym->value;
                                break;
                        }
                        r->done = 0;
                        o = 0;
                        if(thechar != '6')
                                o = r->add;
                        break;
                case R_TLS_LE:
                        if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) {
                                r->done = 0;
                                r->sym = ctxt->gmsym;
                                r->xsym = ctxt->gmsym;
                                r->xadd = r->add;
                                o = 0;
                                if(thechar != '6')
                                        o = r->add;
                                break;
                        }
                        o = ctxt->tlsoffset + r->add;
                        break;
                case R_TLS_IE:
                        if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) {
                                r->done = 0;
                                r->sym = ctxt->gmsym;
                                r->xsym = ctxt->gmsym;
                                r->xadd = r->add;
                                o = 0;
                                if(thechar != '6')
                                        o = r->add;
                                break;
                        }
                        if(iself || ctxt->headtype == Hplan9)
                                o = ctxt->tlsoffset + r->add;
                        else if(ctxt->headtype == Hwindows)
                                o = r->add;
                        else
                                sysfatal("unexpected R_TLS_IE relocation for %s", headstr(ctxt->headtype));
                        break;
                case R_ADDR:
                        if(linkmode == LinkExternal && r->sym->type != SCONST) {
                                r->done = 0;
                                
                                rs = r->sym;
                                r->xadd = r->add;
                                while(rs->outer != nil) {
                                        r->xadd += symaddr(rs) - symaddr(rs->outer);
                                        rs = rs->outer;
                                }
                                if(rs->type != SHOSTOBJ && rs->type != SDYNIMPORT && rs->sect == nil)
                                        diag("missing section for %s", rs->name);
                                r->xsym = rs;
                                o = r->xadd;
                                if(iself) {
                                        if(thechar == '6')
                                                o = 0;
                                } else if(HEADTYPE == Hdarwin) {
                                        if(rs->type != SHOSTOBJ)
                                                o += symaddr(rs);
                                } else {
                                        diag("unhandled pcrel relocation for %s", headstring);
                                }
                                break;
                        }
                        o = symaddr(r->sym) + r->add;
                        
                        
                        
                        
                        
                        if((int32)o < 0 && PtrSize > 4 && siz == 4) {
                                diag("non-pc-relative relocation address is too big: %#llux", o);
                                errorexit();
                        }
                        break;
                case R_CALL:
                case R_PCREL:
                        
                        if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != ctxt->cursym->sect) {
                                r->done = 0;
                                
                                rs = r->sym;
                                r->xadd = r->add;
                                while(rs->outer != nil) {
                                        r->xadd += symaddr(rs) - symaddr(rs->outer);
                                        rs = rs->outer;
                                }
                                r->xadd -= r->siz; 
                                if(rs->type != SHOSTOBJ && rs->type != SDYNIMPORT && rs->sect == nil)
                                        diag("missing section for %s", rs->name);
                                r->xsym = rs;
                                o = r->xadd;
                                if(iself) {
                                        if(thechar == '6')
                                                o = 0;
                                } else if(HEADTYPE == Hdarwin) {
                                        if(rs->type != SHOSTOBJ)
                                                o += symaddr(rs) - rs->sect->vaddr;
                                        o -= r->off; 
                                } else {
                                        diag("unhandled pcrel relocation for %s", headstring);
                                }
                                break;
                        }
                        o = 0;
                        if(r->sym)
                                o += symaddr(r->sym);
                        
                        
                        
                        
                        
                        
                        o += r->add - (s->value + r->off + (int32)r->siz);
                        break;
                case R_SIZE:
                        o = r->sym->size + r->add;
                        break;
                }
                switch(siz) {
                default:
                        ctxt->cursym = s;
                        diag("bad reloc size %#ux for %s", siz, r->sym->name);
                case 1:
                        
                        s->p[off] = (int8)o;
                        break;
                case 4:
                        if(r->type == R_PCREL || r->type == R_CALL) {
                                if(o != (int32)o)
                                        diag("pc-relative relocation address is too big: %#llx", o);
                        } else {
                                if(o != (int32)o && o != (uint32)o)
                                        diag("non-pc-relative relocation address is too big: %#llux", o);
                        }
                        fl = o;
                        cast = (uchar*)&fl;
                        for(i=0; i<4; i++)
                                s->p[off+i] = cast[inuxi4[i]];
                        break;
                case 8:
                        cast = (uchar*)&o;
                        for(i=0; i<8; i++)
                                s->p[off+i] = cast[inuxi8[i]];
                        break;
                }
        }
}
void
reloc(void)
{
        LSym *s;
        if(debug['v'])
                Bprint(&bso, "%5.2f reloc\n", cputime());
        Bflush(&bso);
        for(s=ctxt->textp; s!=S; s=s->next)
                relocsym(s);
        for(s=datap; s!=S; s=s->next)
                relocsym(s);
}
void
dynrelocsym(LSym *s)
{
        Reloc *r;
        if(HEADTYPE == Hwindows) {
                LSym *rel, *targ;
                rel = linklookup(ctxt, ".rel", 0);
                if(s == rel)
                        return;
                for(r=s->r; r<s->r+s->nr; r++) {
                        targ = r->sym;
                        if(targ == nil)
                                continue;
                        if(!targ->reachable)
                                diag("internal inconsistency: dynamic symbol %s is not reachable.", targ->name);
                        if(r->sym->plt == -2 && r->sym->got != -2) { 
                                targ->plt = rel->size;
                                r->sym = rel;
                                r->add = targ->plt;
                                
                                if(thechar == '8') {
                                        adduint8(ctxt, rel, 0xff);
                                        adduint8(ctxt, rel, 0x25);
                                        addaddr(ctxt, rel, targ);
                                        adduint8(ctxt, rel, 0x90);
                                        adduint8(ctxt, rel, 0x90);
                                } else {
                                        adduint8(ctxt, rel, 0xff);
                                        adduint8(ctxt, rel, 0x24);
                                        adduint8(ctxt, rel, 0x25);
                                        addaddrplus4(ctxt, rel, targ, 0);
                                        adduint8(ctxt, rel, 0x90);
                                }
                        } else if(r->sym->plt >= 0) {
                                r->sym = rel;
                                r->add = targ->plt;
                        }
                }
                return;
        }
        for(r=s->r; r<s->r+s->nr; r++) {
                if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) {
                        if(r->sym != S && !r->sym->reachable)
                                diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name);
                        adddynrel(s, r);
                }
        }
}
void
dynreloc(void)
{
        LSym *s;
        
        
        if(debug['d'] && HEADTYPE != Hwindows)
                return;
        if(debug['v'])
                Bprint(&bso, "%5.2f reloc\n", cputime());
        Bflush(&bso);
        for(s=ctxt->textp; s!=S; s=s->next)
                dynrelocsym(s);
        for(s=datap; s!=S; s=s->next)
                dynrelocsym(s);
        if(iself)
                elfdynhash();
}
static void
blk(LSym *start, int32 addr, int32 size)
{
        LSym *sym;
        int32 eaddr;
        uchar *p, *ep;
        for(sym = start; sym != nil; sym = sym->next)
                if(!(sym->type&SSUB) && sym->value >= addr)
                        break;
        eaddr = addr+size;
        for(; sym != nil; sym = sym->next) {
                if(sym->type&SSUB)
                        continue;
                if(sym->value >= eaddr)
                        break;
                if(sym->value < addr) {
                        diag("phase error: addr=%#llx but sym=%#llx type=%d", (vlong)addr, (vlong)sym->value, sym->type);
                        errorexit();
                }
                ctxt->cursym = sym;
                for(; addr < sym->value; addr++)
                        cput(0);
                p = sym->p;
                ep = p + sym->np;
                while(p < ep)
                        cput(*p++);
                addr += sym->np;
                for(; addr < sym->value+sym->size; addr++)
                        cput(0);
                if(addr != sym->value+sym->size) {
                        diag("phase error: addr=%#llx value+size=%#llx", (vlong)addr, (vlong)sym->value+sym->size);
                        errorexit();
                }
        }
        for(; addr < eaddr; addr++)
                cput(0);
        cflush();
}
void
codeblk(int32 addr, int32 size)
{
        LSym *sym;
        int32 eaddr, n;
        uchar *q;
        if(debug['a'])
                Bprint(&bso, "codeblk [%#x,%#x) at offset %#llx\n", addr, addr+size, cpos());
        blk(ctxt->textp, addr, size);
        
        if(!debug['a'])
                return;
        for(sym = ctxt->textp; sym != nil; sym = sym->next) {
                if(!sym->reachable)
                        continue;
                if(sym->value >= addr)
                        break;
        }
        eaddr = addr + size;
        for(; sym != nil; sym = sym->next) {
                if(!sym->reachable)
                        continue;
                if(sym->value >= eaddr)
                        break;
                if(addr < sym->value) {
                        Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr);
                        for(; addr < sym->value; addr++)
                                Bprint(&bso, " %.2ux", 0);
                        Bprint(&bso, "\n");
                }
                Bprint(&bso, "%.6llux\t%-20s\n", (vlong)addr, sym->name);
                n = sym->size;
                q = sym->p;
                while(n >= 16) {
                        Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q);
                        addr += 16;
                        q += 16;
                        n -= 16;
                }
                if(n > 0)
                        Bprint(&bso, "%.6ux\t%-20.*I\n", addr, (int)n, q);
                addr += n;
        }
        if(addr < eaddr) {
                Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr);
                for(; addr < eaddr; addr++)
                        Bprint(&bso, " %.2ux", 0);
        }
        Bflush(&bso);
}
void
datblk(int32 addr, int32 size)
{
        LSym *sym;
        int32 i, eaddr;
        uchar *p, *ep;
        char *typ, *rsname;
        Reloc *r;
        if(debug['a'])
                Bprint(&bso, "datblk [%#x,%#x) at offset %#llx\n", addr, addr+size, cpos());
        blk(datap, addr, size);
        
        if(!debug['a'])
                return;
        for(sym = datap; sym != nil; sym = sym->next)
                if(sym->value >= addr)
                        break;
        eaddr = addr + size;
        for(; sym != nil; sym = sym->next) {
                if(sym->value >= eaddr)
                        break;
                if(addr < sym->value) {
                        Bprint(&bso, "\t%.8ux| 00 ...\n", addr);
                        addr = sym->value;
                }
                Bprint(&bso, "%s\n\t%.8ux|", sym->name, (uint)addr);
                p = sym->p;
                ep = p + sym->np;
                while(p < ep) {
                        if(p > sym->p && (int)(p-sym->p)%16 == 0)
                                Bprint(&bso, "\n\t%.8ux|", (uint)(addr+(p-sym->p)));
                        Bprint(&bso, " %.2ux", *p++);
                }
                addr += sym->np;
                for(; addr < sym->value+sym->size; addr++)
                        Bprint(&bso, " %.2ux", 0);
                Bprint(&bso, "\n");
                
                if(linkmode == LinkExternal) {
                        for(i=0; i<sym->nr; i++) {
                                r = &sym->r[i];
                                rsname = "";
                                if(r->sym)
                                        rsname = r->sym->name;
                                typ = "?";
                                switch(r->type) {
                                case R_ADDR:
                                        typ = "addr";
                                        break;
                                case R_PCREL:
                                        typ = "pcrel";
                                        break;
                                case R_CALL:
                                        typ = "call";
                                        break;
                                }
                                Bprint(&bso, "\treloc %.8ux/%d %s %s+%#llx [%#llx]\n",
                                        (uint)(sym->value+r->off), r->siz, typ, rsname, (vlong)r->add, (vlong)(r->sym->value+r->add));
                        }
                }                               
        }
        if(addr < eaddr)
                Bprint(&bso, "\t%.8ux| 00 ...\n", (uint)addr);
        Bprint(&bso, "\t%.8ux|\n", (uint)eaddr);
}
void
strnput(char *s, int n)
{
        for(; n > 0 && *s; s++) {
                cput(*s);
                n--;
        }
        while(n > 0) {
                cput(0);
                n--;
        }
}
void
addstrdata(char *name, char *value)
{
        LSym *s, *sp;
        char *p;
        p = smprint("%s.str", name);
        sp = linklookup(ctxt, p, 0);
        free(p);
        addstring(sp, value);
        s = linklookup(ctxt, name, 0);
        s->size = 0;
        s->dupok = 1;
        addaddr(ctxt, s, sp);
        adduint32(ctxt, s, strlen(value));
        if(PtrSize == 8)
                adduint32(ctxt, s, 0);  
        
        sp->reachable = s->reachable;
}
vlong
addstring(LSym *s, char *str)
{
        int n;
        int32 r;
        if(s->type == 0)
                s->type = SNOPTRDATA;
        s->reachable = 1;
        r = s->size;
        n = strlen(str)+1;
        if(strcmp(s->name, ".shstrtab") == 0)
                elfsetstring(str, r);
        symgrow(ctxt, s, r+n);
        memmove(s->p+r, str, n);
        s->size += n;
        return r;
}
void
dosymtype(void)
{
        LSym *s;
        for(s = ctxt->allsym; s != nil; s = s->allsym) {
                if(s->np > 0) {
                        if(s->type == SBSS)
                                s->type = SDATA;
                        if(s->type == SNOPTRBSS)
                                s->type = SNOPTRDATA;
                }
        }
}
static int32
symalign(LSym *s)
{
        int32 align;
        if(s->align != 0)
                return s->align;
        align = MaxAlign;
        while(align > s->size && align > 1)
                align >>= 1;
        if(align < s->align)
                align = s->align;
        return align;
}
        
static vlong
aligndatsize(vlong datsize, LSym *s)
{
        return rnd(datsize, symalign(s));
}
static int32
maxalign(LSym *s, int type)
{
        int32 align, max;
        
        max = 0;
        for(; s != S && s->type <= type; s = s->next) {
                align = symalign(s);
                if(max < align)
                        max = align;
        }
        return max;
}
static void
gcaddsym(LSym *gc, LSym *s, vlong off)
{
        vlong a;
        LSym *gotype;
        if(s->size < PtrSize)
                return;
        if(strcmp(s->name, ".string") == 0)
                return;
        gotype = s->gotype;
        if(gotype != nil) {
                
                adduintxx(ctxt, gc, GC_CALL, PtrSize);
                adduintxx(ctxt, gc, off, PtrSize);
                addpcrelplus(ctxt, gc, decodetype_gc(gotype), 3*PtrSize+4);
                if(PtrSize == 8)
                        adduintxx(ctxt, gc, 0, 4);
        } else {
                
                for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) {
                        adduintxx(ctxt, gc, GC_APTR, PtrSize);
                        adduintxx(ctxt, gc, off+a, PtrSize);
                }
        }
}
void
growdatsize(vlong *datsizep, LSym *s)
{
        vlong datsize;
        
        datsize = *datsizep;
        if(s->size < 0)
                diag("negative size (datsize = %lld, s->size = %lld)", datsize, s->size);
        if(datsize + s->size < datsize)
                diag("symbol too large (datsize = %lld, s->size = %lld)", datsize, s->size);
        *datsizep = datsize + s->size;
}
void
dodata(void)
{
        int32 n;
        vlong datsize;
        Section *sect;
        Segment *segro;
        LSym *s, *last, **l;
        LSym *gcdata1, *gcbss1;
        if(debug['v'])
                Bprint(&bso, "%5.2f dodata\n", cputime());
        Bflush(&bso);
        gcdata1 = linklookup(ctxt, "gcdata", 0);
        gcbss1 = linklookup(ctxt, "gcbss", 0);
        
        adduintxx(ctxt, gcdata1, 0, PtrSize);
        adduintxx(ctxt, gcbss1, 0, PtrSize);
        last = nil;
        datap = nil;
        for(s=ctxt->allsym; s!=S; s=s->allsym) {
                if(!s->reachable || s->special)
                        continue;
                if(STEXT < s->type && s->type < SXREF) {
                        if(s->onlist)
                                sysfatal("symbol %s listed multiple times", s->name);
                        s->onlist = 1;
                        if(last == nil)
                                datap = s;
                        else
                                last->next = s;
                        s->next = nil;
                        last = s;
                }
        }
        for(s = datap; s != nil; s = s->next) {
                if(s->np > s->size)
                        diag("%s: initialize bounds (%lld < %d)",
                                s->name, (vlong)s->size, s->np);
        }
        
        if(HEADTYPE == Hdarwin)
                machosymorder();
        dynreloc();
        
        for(l=&datap; (s=*l) != nil; ) {
                if(s->type <= STEXT || SXREF <= s->type)
                        *l = s->next;
                else
                        l = &s->next;
        }
        *l = nil;
        datap = listsort(datap, datcmp, offsetof(LSym, next));
        
        
        
        s = datap;
        for(; s != nil && s->type < SELFSECT; s = s->next)
                ;
        
        datsize = 0;
        for(; s != nil && s->type < SNOPTRDATA; s = s->next) {
                sect = addsection(&segdata, s->name, 06);
                sect->align = symalign(s);
                datsize = rnd(datsize, sect->align);
                sect->vaddr = datsize;
                s->sect = sect;
                s->type = SDATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
                sect->len = datsize - sect->vaddr;
        }
        
        sect = addsection(&segdata, ".noptrdata", 06);
        sect->align = maxalign(s, SINITARR-1);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "noptrdata", 0)->sect = sect;
        linklookup(ctxt, "enoptrdata", 0)->sect = sect;
        for(; s != nil && s->type < SINITARR; s = s->next) {
                datsize = aligndatsize(datsize, s);
                s->sect = sect;
                s->type = SDATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        
        if(flag_shared) {
                sect = addsection(&segdata, ".init_array", 06);
                sect->align = maxalign(s, SINITARR);
                datsize = rnd(datsize, sect->align);
                sect->vaddr = datsize;
                for(; s != nil && s->type == SINITARR; s = s->next) {
                        datsize = aligndatsize(datsize, s);
                        s->sect = sect;
                        s->value = datsize - sect->vaddr;
                        growdatsize(&datsize, s);
                }
                sect->len = datsize - sect->vaddr;
        }
        
        sect = addsection(&segdata, ".data", 06);
        sect->align = maxalign(s, SBSS-1);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "data", 0)->sect = sect;
        linklookup(ctxt, "edata", 0)->sect = sect;
        for(; s != nil && s->type < SBSS; s = s->next) {
                if(s->type == SINITARR) {
                        ctxt->cursym = s;
                        diag("unexpected symbol type %d", s->type);
                }
                s->sect = sect;
                s->type = SDATA;
                datsize = aligndatsize(datsize, s);
                s->value = datsize - sect->vaddr;
                gcaddsym(gcdata1, s, datsize - sect->vaddr);  
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        adduintxx(ctxt, gcdata1, GC_END, PtrSize);
        setuintxx(ctxt, gcdata1, 0, sect->len, PtrSize);
        
        sect = addsection(&segdata, ".bss", 06);
        sect->align = maxalign(s, SNOPTRBSS-1);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "bss", 0)->sect = sect;
        linklookup(ctxt, "ebss", 0)->sect = sect;
        for(; s != nil && s->type < SNOPTRBSS; s = s->next) {
                s->sect = sect;
                datsize = aligndatsize(datsize, s);
                s->value = datsize - sect->vaddr;
                gcaddsym(gcbss1, s, datsize - sect->vaddr);  
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        adduintxx(ctxt, gcbss1, GC_END, PtrSize);
        setuintxx(ctxt, gcbss1, 0, sect->len, PtrSize);
        
        sect = addsection(&segdata, ".noptrbss", 06);
        sect->align = maxalign(s, SNOPTRBSS);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "noptrbss", 0)->sect = sect;
        linklookup(ctxt, "enoptrbss", 0)->sect = sect;
        for(; s != nil && s->type == SNOPTRBSS; s = s->next) {
                datsize = aligndatsize(datsize, s);
                s->sect = sect;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        linklookup(ctxt, "end", 0)->sect = sect;
        
        if(datsize != (uint32)datsize) {
                diag("data or bss segment too large");
        }
        
        if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd) {
                sect = addsection(&segdata, ".tbss", 06);
                sect->align = PtrSize;
                sect->vaddr = 0;
                datsize = 0;
                for(; s != nil && s->type == STLSBSS; s = s->next) {
                        datsize = aligndatsize(datsize, s);
                        s->sect = sect;
                        s->value = datsize - sect->vaddr;
                        growdatsize(&datsize, s);
                }
                sect->len = datsize;
        } else {
                
                
                
                if(s != nil && s->type == STLSBSS && strcmp(s->name, "runtime.tlsgm") == 0) {
                        s->value = 0;
                        s = s->next;
                }
        }
        
        if(s != nil) {
                ctxt->cursym = nil;
                diag("unexpected symbol type %d for %s", s->type, s->name);
        }
        
        if(iself && linkmode == LinkInternal)
                segro = &segrodata;
        else
                segro = &segtext;
        s = datap;
        
        datsize = 0;
        
        
        for(; s != nil && s->type < STYPE; s = s->next) {
                sect = addsection(&segtext, s->name, 04);
                sect->align = symalign(s);
                datsize = rnd(datsize, sect->align);
                sect->vaddr = datsize;
                s->sect = sect;
                s->type = SRODATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
                sect->len = datsize - sect->vaddr;
        }
        
        sect = addsection(segro, ".rodata", 04);
        sect->align = maxalign(s, STYPELINK-1);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = 0;
        linklookup(ctxt, "rodata", 0)->sect = sect;
        linklookup(ctxt, "erodata", 0)->sect = sect;
        for(; s != nil && s->type < STYPELINK; s = s->next) {
                datsize = aligndatsize(datsize, s);
                s->sect = sect;
                s->type = SRODATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        
        sect = addsection(segro, ".typelink", 04);
        sect->align = maxalign(s, STYPELINK);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "typelink", 0)->sect = sect;
        linklookup(ctxt, "etypelink", 0)->sect = sect;
        for(; s != nil && s->type == STYPELINK; s = s->next) {
                datsize = aligndatsize(datsize, s);
                s->sect = sect;
                s->type = SRODATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        
        sect = addsection(segro, ".gosymtab", 04);
        sect->align = maxalign(s, SPCLNTAB-1);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "symtab", 0)->sect = sect;
        linklookup(ctxt, "esymtab", 0)->sect = sect;
        for(; s != nil && s->type < SPCLNTAB; s = s->next) {
                datsize = aligndatsize(datsize, s);
                s->sect = sect;
                s->type = SRODATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        
        sect = addsection(segro, ".gopclntab", 04);
        sect->align = maxalign(s, SELFROSECT-1);
        datsize = rnd(datsize, sect->align);
        sect->vaddr = datsize;
        linklookup(ctxt, "pclntab", 0)->sect = sect;
        linklookup(ctxt, "epclntab", 0)->sect = sect;
        for(; s != nil && s->type < SELFROSECT; s = s->next) {
                datsize = aligndatsize(datsize, s);
                s->sect = sect;
                s->type = SRODATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
        }
        sect->len = datsize - sect->vaddr;
        
        for(; s != nil && s->type < SELFSECT; s = s->next) {
                sect = addsection(segro, s->name, 04);
                sect->align = symalign(s);
                datsize = rnd(datsize, sect->align);
                sect->vaddr = datsize;
                s->sect = sect;
                s->type = SRODATA;
                s->value = datsize - sect->vaddr;
                growdatsize(&datsize, s);
                sect->len = datsize - sect->vaddr;
        }
        
        if(datsize != (uint32)datsize) {
                diag("read-only data segment too large");
        }
        
        
        n = 1;
        for(sect = segtext.sect; sect != nil; sect = sect->next)
                sect->extnum = n++;
        for(sect = segrodata.sect; sect != nil; sect = sect->next)
                sect->extnum = n++;
        for(sect = segdata.sect; sect != nil; sect = sect->next)
                sect->extnum = n++;
}
void
textaddress(void)
{
        uvlong va;
        Section *sect;
        LSym *sym, *sub;
        addsection(&segtext, ".text", 05);
        
        
        
        sect = segtext.sect;
        sect->align = funcalign;
        linklookup(ctxt, "text", 0)->sect = sect;
        linklookup(ctxt, "etext", 0)->sect = sect;
        va = INITTEXT;
        sect->vaddr = va;
        for(sym = ctxt->textp; sym != nil; sym = sym->next) {
                sym->sect = sect;
                if(sym->type & SSUB)
                        continue;
                if(sym->align != 0)
                        va = rnd(va, sym->align);
                else
                        va = rnd(va, funcalign);
                sym->value = 0;
                for(sub = sym; sub != S; sub = sub->sub)
                        sub->value += va;
                if(sym->size == 0 && sym->sub != S)
                        ctxt->cursym = sym;
                va += sym->size;
        }
        sect->len = va - sect->vaddr;
}
void
address(void)
{
        Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss;
        Section *typelink;
        LSym *sym, *sub;
        uvlong va;
        vlong vlen;
        va = INITTEXT;
        segtext.rwx = 05;
        segtext.vaddr = va;
        segtext.fileoff = HEADR;
        for(s=segtext.sect; s != nil; s=s->next) {
                va = rnd(va, s->align);
                s->vaddr = va;
                va += s->len;
        }
        segtext.len = va - INITTEXT;
        segtext.filelen = segtext.len;
        if(HEADTYPE == Hnacl)
                va += 32; 
        if(segrodata.sect != nil) {
                
                
                va = rnd(va, INITRND);
                segrodata.rwx = 04;
                segrodata.vaddr = va;
                segrodata.fileoff = va - segtext.vaddr + segtext.fileoff;
                segrodata.filelen = 0;
                for(s=segrodata.sect; s != nil; s=s->next) {
                        va = rnd(va, s->align);
                        s->vaddr = va;
                        va += s->len;
                }
                segrodata.len = va - segrodata.vaddr;
                segrodata.filelen = segrodata.len;
        }
        va = rnd(va, INITRND);
        segdata.rwx = 06;
        segdata.vaddr = va;
        segdata.fileoff = va - segtext.vaddr + segtext.fileoff;
        segdata.filelen = 0;
        if(HEADTYPE == Hwindows)
                segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN);
        if(HEADTYPE == Hplan9)
                segdata.fileoff = segtext.fileoff + segtext.filelen;
        data = nil;
        noptr = nil;
        bss = nil;
        noptrbss = nil;
        for(s=segdata.sect; s != nil; s=s->next) {
                vlen = s->len;
                if(s->next)
                        vlen = s->next->vaddr - s->vaddr;
                s->vaddr = va;
                va += vlen;
                segdata.len = va - segdata.vaddr;
                if(strcmp(s->name, ".data") == 0)
                        data = s;
                if(strcmp(s->name, ".noptrdata") == 0)
                        noptr = s;
                if(strcmp(s->name, ".bss") == 0)
                        bss = s;
                if(strcmp(s->name, ".noptrbss") == 0)
                        noptrbss = s;
        }
        segdata.filelen = bss->vaddr - segdata.vaddr;
        text = segtext.sect;
        if(segrodata.sect)
                rodata = segrodata.sect;
        else
                rodata = text->next;
        typelink = rodata->next;
        symtab = typelink->next;
        pclntab = symtab->next;
        for(sym = datap; sym != nil; sym = sym->next) {
                ctxt->cursym = sym;
                if(sym->sect != nil)
                        sym->value += sym->sect->vaddr;
                for(sub = sym->sub; sub != nil; sub = sub->sub)
                        sub->value += sym->value;
        }
        xdefine("text", STEXT, text->vaddr);
        xdefine("etext", STEXT, text->vaddr + text->len);
        xdefine("rodata", SRODATA, rodata->vaddr);
        xdefine("erodata", SRODATA, rodata->vaddr + rodata->len);
        xdefine("typelink", SRODATA, typelink->vaddr);
        xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len);
        sym = linklookup(ctxt, "gcdata", 0);
        xdefine("egcdata", SRODATA, symaddr(sym) + sym->size);
        linklookup(ctxt, "egcdata", 0)->sect = sym->sect;
        sym = linklookup(ctxt, "gcbss", 0);
        xdefine("egcbss", SRODATA, symaddr(sym) + sym->size);
        linklookup(ctxt, "egcbss", 0)->sect = sym->sect;
        xdefine("symtab", SRODATA, symtab->vaddr);
        xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len);
        xdefine("pclntab", SRODATA, pclntab->vaddr);
        xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len);
        xdefine("noptrdata", SNOPTRDATA, noptr->vaddr);
        xdefine("enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len);
        xdefine("bss", SBSS, bss->vaddr);
        xdefine("ebss", SBSS, bss->vaddr + bss->len);
        xdefine("data", SDATA, data->vaddr);
        xdefine("edata", SDATA, data->vaddr + data->len);
        xdefine("noptrbss", SNOPTRBSS, noptrbss->vaddr);
        xdefine("enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len);
        xdefine("end", SBSS, segdata.vaddr + segdata.len);
}