// Inferno utils/8l/asm.c // http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.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. // Data layout and relocation. #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/macho.h" #include "../ld/pe.h" #include "../../runtime/mgc0.h" void dynreloc(void); /* * divide-and-conquer list-link * sort of LSym* structures. * Used for the data block. */ 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); /* set up lead element */ 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; rr+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 | SHIDDEN)) == 0 || (r->sym->type & SMASK) == SXREF)) { diag("%s: not defined", r->sym->name); continue; } if(r->type >= 256) continue; if(r->siz == 0) // informational relocation - no work to do continue; // Solaris needs the ability to reference dynimport symbols. 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); // Android emulates runtime.tlsg as a regular variable. if (r->type == R_TLS && strcmp(goos, "android") == 0) r->type = R_ADDR; 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') { // On ELF ARM, the thread pointer is 8 bytes before // the start of the thread-local data block, so add 8 // to the actual TLS offset (r->sym->value). // This 8 seems to be a fundamental constant of // ELF on ARM (or maybe Glibc on ARM); it is not // related to the fact that our own TLS storage happens // to take up 8 bytes. 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->tlsg; r->xsym = ctxt->tlsg; 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->tlsg; r->xsym = ctxt->tlsg; 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; // set up addend for eventual relocation via outer symbol. 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; // On amd64, 4-byte offsets will be sign-extended, so it is impossible to // access more than 2GB of static data; fail at link time is better than // fail at runtime. See http://golang.org/issue/7980. // Instead of special casing only amd64, we treat this as an error on all // 64-bit architectures so as to be future-proof. 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: // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != ctxt->cursym->sect) { r->done = 0; // set up addend for eventual relocation via outer symbol. 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; // relative to address after the relocated chunk 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(r->type == R_CALL) { if(rs->type != SHOSTOBJ) o += symaddr(rs) - rs->sect->vaddr; o -= r->off; // relative to section offset, not symbol } else { o += r->siz; } } else { diag("unhandled pcrel relocation for %s", headstring); } break; } o = 0; if(r->sym) o += symaddr(r->sym); // NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c // compiler. The expression s->value + r->off + r->siz is int32 + int32 + // uchar, and Plan 9 8c incorrectly treats the expression as type uint32 // instead of int32, causing incorrect values when sign extended for adding // to o. The bug only occurs on Plan 9, because this C program is compiled by // the standard host compiler (gcc on most other systems). o += r->add - (s->value + r->off + (int32)r->siz); break; case R_SIZE: o = r->sym->size + r->add; break; } //print("relocate %s %#llux (%#llux+%#llux, size %d) => %s %#llux +%#llx [%llx]\n", s->name, (uvlong)(s->value+off), (uvlong)s->value, (uvlong)r->off, r->siz, r->sym ? r->sym->name : "", (uvlong)symaddr(r->sym), (vlong)r->add, (vlong)o); switch(siz) { default: ctxt->cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); case 1: // TODO(rsc): Remove. 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; rr+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) { // make dynimport JMP table for PE object files. targ->plt = rel->size; r->sym = rel; r->add = targ->plt; // jmp *addr 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; rr+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; // -d suppresses dynamic loader format, so we may as well not // compute these sections or mark their symbols as reachable. 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, int64 addr, int64 size) { LSym *sym; int64 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(int64 addr, int64 size) { LSym *sym; int64 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); /* again for printing */ 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(int64 addr, int64 size) { LSym *sym; int64 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); /* again for printing */ 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; inr; 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; uchar reachable; p = smprint("%s.str", name); sp = linklookup(ctxt, p, 0); free(p); addstring(sp, value); sp->type = SRODATA; s = linklookup(ctxt, name, 0); s->size = 0; s->dupok = 1; reachable = s->reachable; addaddr(ctxt, s, sp); adduintxx(ctxt, s, strlen(value), PtrSize); // addstring, addaddr, etc., mark the symbols as reachable. // In this case that is not necessarily true, so stick to what // we know before entering this function. s->reachable = reachable; sp->reachable = 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)); } // maxalign returns the maximum required alignment for // the list of symbols s; the list stops when s->type exceeds type. 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; } // Helper object for building GC type programs. typedef struct ProgGen ProgGen; struct ProgGen { LSym* s; int32 datasize; uint8 data[256/PointersPerByte]; vlong pos; }; static void proggeninit(ProgGen *g, LSym *s) { g->s = s; g->datasize = 0; g->pos = 0; memset(g->data, 0, sizeof(g->data)); } static void proggenemit(ProgGen *g, uint8 v) { adduint8(ctxt, g->s, v); } // Writes insData block from g->data. static void proggendataflush(ProgGen *g) { int32 i, s; if(g->datasize == 0) return; proggenemit(g, insData); proggenemit(g, g->datasize); s = (g->datasize + PointersPerByte - 1)/PointersPerByte; for(i = 0; i < s; i++) proggenemit(g, g->data[i]); g->datasize = 0; memset(g->data, 0, sizeof(g->data)); } static void proggendata(ProgGen *g, uint8 d) { g->data[g->datasize/PointersPerByte] |= d << ((g->datasize%PointersPerByte)*BitsPerPointer); g->datasize++; if(g->datasize == 255) proggendataflush(g); } // Skip v bytes due to alignment, etc. static void proggenskip(ProgGen *g, vlong off, vlong v) { vlong i; for(i = off; i < off+v; i++) { if((i%PtrSize) == 0) proggendata(g, BitsScalar); } } // Emit insArray instruction. static void proggenarray(ProgGen *g, vlong len) { int32 i; proggendataflush(g); proggenemit(g, insArray); for(i = 0; i < PtrSize; i++, len >>= 8) proggenemit(g, len); } static void proggenarrayend(ProgGen *g) { proggendataflush(g); proggenemit(g, insArrayEnd); } static void proggenfini(ProgGen *g, vlong size) { proggenskip(g, g->pos, size - g->pos); proggendataflush(g); proggenemit(g, insEnd); } // This function generates GC pointer info for global variables. static void proggenaddsym(ProgGen *g, LSym *s) { LSym *gcprog; uint8 *mask; vlong i, size; if(s->size == 0) return; // Skip alignment hole from the previous symbol. proggenskip(g, g->pos, s->value - g->pos); g->pos += s->value - g->pos; // The test for names beginning with . here is meant // to keep .dynamic and .dynsym from turning up as // conservative symbols. They should be marked SELFSECT // and not SDATA, but sometimes that doesn't happen. // Leave debugging the SDATA issue for the Go rewrite. if(s->gotype == nil && s->size >= PtrSize && s->name[0] != '.') { // conservative scan diag("missing Go type information for global symbol: %s size %d", s->name, (int)s->size); if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); size = (s->size+PtrSize-1)/PtrSize*PtrSize; if(size < 32*PtrSize) { // Emit small symbols as data. for(i = 0; i < size/PtrSize; i++) proggendata(g, BitsPointer); } else { // Emit large symbols as array. proggenarray(g, size/PtrSize); proggendata(g, BitsPointer); proggenarrayend(g); } g->pos = s->value + size; } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize || s->name[0] == '.') { // no scan if(s->size < 32*PtrSize) { // Emit small symbols as data. // This case also handles unaligned and tiny symbols, so tread carefully. for(i = s->value; i < s->value+s->size; i++) { if((i%PtrSize) == 0) proggendata(g, BitsScalar); } } else { // Emit large symbols as array. if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned noscan symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); proggenarray(g, s->size/PtrSize); proggendata(g, BitsScalar); proggenarrayend(g); } g->pos = s->value + s->size; } else if(decodetype_usegcprog(s->gotype)) { // gc program, copy directly proggendataflush(g); gcprog = decodetype_gcprog(s->gotype); size = decodetype_size(s->gotype); if((size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned gcprog symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); for(i = 0; i < gcprog->np-1; i++) proggenemit(g, gcprog->p[i]); g->pos = s->value + size; } else { // gc mask, it's small so emit as data mask = decodetype_gcmask(s->gotype); size = decodetype_size(s->gotype); if((size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned gcmask symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); for(i = 0; i < size; i += PtrSize) proggendata(g, (mask[i/PtrSize/2]>>((i/PtrSize%2)*4+2))&BitsMask); g->pos = s->value + size; } } 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 *gcdata, *gcbss; ProgGen gen; if(debug['v']) Bprint(&bso, "%5.2f dodata\n", cputime()); Bflush(&bso); 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); } /* * now that we have the datap list, but before we start * to assign addresses, record all the necessary * dynamic relocations. these will grow the relocation * symbol, which is itself data. * * on darwin, we need the symbol table numbers for dynreloc. */ if(HEADTYPE == Hdarwin) machosymorder(); dynreloc(); /* some symbols may no longer belong in datap (Mach-O) */ 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)); /* * allocate sections. list is sorted by type, * so we can just walk it for each piece we want to emit. * segdata is processed before segtext, because we need * to see all symbols in the .data and .bss sections in order * to generate garbage collection information. */ /* begin segdata */ /* skip symbols belonging to segtext */ s = datap; for(; s != nil && s->type < SELFSECT; s = s->next) ; /* writable ELF sections */ 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; } /* pointer-free data */ sect = addsection(&segdata, ".noptrdata", 06); sect->align = maxalign(s, SINITARR-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.noptrdata", 0)->sect = sect; linklookup(ctxt, "runtime.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; /* shared library initializer */ 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; } /* data */ sect = addsection(&segdata, ".data", 06); sect->align = maxalign(s, SBSS-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.data", 0)->sect = sect; linklookup(ctxt, "runtime.edata", 0)->sect = sect; gcdata = linklookup(ctxt, "runtime.gcdata", 0); proggeninit(&gen, gcdata); 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; proggenaddsym(&gen, s); // gc growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; proggenfini(&gen, sect->len); // gc /* bss */ sect = addsection(&segdata, ".bss", 06); sect->align = maxalign(s, SNOPTRBSS-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.bss", 0)->sect = sect; linklookup(ctxt, "runtime.ebss", 0)->sect = sect; gcbss = linklookup(ctxt, "runtime.gcbss", 0); proggeninit(&gen, gcbss); for(; s != nil && s->type < SNOPTRBSS; s = s->next) { s->sect = sect; datsize = aligndatsize(datsize, s); s->value = datsize - sect->vaddr; proggenaddsym(&gen, s); // gc growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; proggenfini(&gen, sect->len); // gc /* pointer-free bss */ sect = addsection(&segdata, ".noptrbss", 06); sect->align = maxalign(s, SNOPTRBSS); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.noptrbss", 0)->sect = sect; linklookup(ctxt, "runtime.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, "runtime.end", 0)->sect = sect; // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. 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 { // Might be internal linking but still using cgo. // In that case, the only possible STLSBSS symbol is runtime.tlsg. // Give it offset 0, because it's the only thing here. if(s != nil && s->type == STLSBSS && strcmp(s->name, "runtime.tlsg") == 0) { s->value = 0; s = s->next; } } if(s != nil) { ctxt->cursym = nil; diag("unexpected symbol type %d for %s", s->type, s->name); } /* * We finished data, begin read-only data. * Not all systems support a separate read-only non-executable data section. * ELF systems do. * OS X and Plan 9 do not. * Windows PE may, but if so we have not implemented it. * And if we're using external linking mode, the point is moot, * since it's not our decision; that code expects the sections in * segtext. */ if(iself && linkmode == LinkInternal) segro = &segrodata; else segro = &segtext; s = datap; datsize = 0; /* read-only executable ELF, Mach-O sections */ 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; } /* read-only data */ sect = addsection(segro, ".rodata", 04); sect->align = maxalign(s, STYPELINK-1); datsize = rnd(datsize, sect->align); sect->vaddr = 0; linklookup(ctxt, "runtime.rodata", 0)->sect = sect; linklookup(ctxt, "runtime.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; /* typelink */ sect = addsection(segro, ".typelink", 04); sect->align = maxalign(s, STYPELINK); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.typelink", 0)->sect = sect; linklookup(ctxt, "runtime.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; /* gosymtab */ sect = addsection(segro, ".gosymtab", 04); sect->align = maxalign(s, SPCLNTAB-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.symtab", 0)->sect = sect; linklookup(ctxt, "runtime.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; /* gopclntab */ sect = addsection(segro, ".gopclntab", 04); sect->align = maxalign(s, SELFROSECT-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; linklookup(ctxt, "runtime.pclntab", 0)->sect = sect; linklookup(ctxt, "runtime.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; /* read-only ELF, Mach-O sections */ 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; } // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if(datsize != (uint32)datsize) { diag("read-only data segment too large"); } /* number the sections */ 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++; } // assign addresses to text void textaddress(void) { uvlong va; Section *sect; LSym *sym, *sub; addsection(&segtext, ".text", 05); // Assign PCs in text segment. // Could parallelize, by assigning to text // and then letting threads copy down, but probably not worth it. sect = segtext.sect; sect->align = funcalign; linklookup(ctxt, "runtime.text", 0)->sect = sect; linklookup(ctxt, "runtime.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; } // assign addresses 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; // room for the "halt sled" if(segrodata.sect != nil) { // align to page boundary so as not to mix // rodata and executable text. 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("runtime.text", STEXT, text->vaddr); xdefine("runtime.etext", STEXT, text->vaddr + text->len); xdefine("runtime.rodata", SRODATA, rodata->vaddr); xdefine("runtime.erodata", SRODATA, rodata->vaddr + rodata->len); xdefine("runtime.typelink", SRODATA, typelink->vaddr); xdefine("runtime.etypelink", SRODATA, typelink->vaddr + typelink->len); sym = linklookup(ctxt, "runtime.gcdata", 0); xdefine("runtime.egcdata", SRODATA, symaddr(sym) + sym->size); linklookup(ctxt, "runtime.egcdata", 0)->sect = sym->sect; sym = linklookup(ctxt, "runtime.gcbss", 0); xdefine("runtime.egcbss", SRODATA, symaddr(sym) + sym->size); linklookup(ctxt, "runtime.egcbss", 0)->sect = sym->sect; xdefine("runtime.symtab", SRODATA, symtab->vaddr); xdefine("runtime.esymtab", SRODATA, symtab->vaddr + symtab->len); xdefine("runtime.pclntab", SRODATA, pclntab->vaddr); xdefine("runtime.epclntab", SRODATA, pclntab->vaddr + pclntab->len); xdefine("runtime.noptrdata", SNOPTRDATA, noptr->vaddr); xdefine("runtime.enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len); xdefine("runtime.bss", SBSS, bss->vaddr); xdefine("runtime.ebss", SBSS, bss->vaddr + bss->len); xdefine("runtime.data", SDATA, data->vaddr); xdefine("runtime.edata", SDATA, data->vaddr + data->len); xdefine("runtime.noptrbss", SNOPTRBSS, noptrbss->vaddr); xdefine("runtime.enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len); xdefine("runtime.end", SBSS, segdata.vaddr + segdata.len); }