// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Mach-O file writing // http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html #include "l.h" #include "../ld/dwarf.h" #include "../ld/lib.h" #include "../ld/macho.h" static int macho64; static MachoHdr hdr; static MachoLoad load[16]; static MachoSeg seg[16]; static MachoDebug xdebug[16]; static int nload, nseg, ndebug, nsect; void machoinit(void) { switch(thechar) { // 64-bit architectures case '6': macho64 = 1; break; // 32-bit architectures default: break; } } MachoHdr* getMachoHdr(void) { return &hdr; } MachoLoad* newMachoLoad(uint32 type, uint32 ndata) { MachoLoad *l; if(nload >= nelem(load)) { diag("too many loads"); errorexit(); } l = &load[nload++]; l->type = type; l->ndata = ndata; l->data = mal(ndata*4); return l; } MachoSeg* newMachoSeg(char *name, int msect) { MachoSeg *s; if(nseg >= nelem(seg)) { diag("too many segs"); errorexit(); } s = &seg[nseg++]; s->name = name; s->msect = msect; s->sect = mal(msect*sizeof s->sect[0]); return s; } MachoSect* newMachoSect(MachoSeg *seg, char *name) { MachoSect *s; if(seg->nsect >= seg->msect) { diag("too many sects in segment %s", seg->name); errorexit(); } s = &seg->sect[seg->nsect++]; s->name = name; nsect++; return s; } MachoDebug* newMachoDebug(void) { if(ndebug >= nelem(xdebug)) { diag("too many debugs"); errorexit(); } return &xdebug[ndebug++]; } // Generic linking code. static uchar *linkdata; static uint32 nlinkdata; static uint32 mlinkdata; static uchar *strtab; static uint32 nstrtab; static uint32 mstrtab; struct Expsym { int off; Sym* s; } *expsym; static int nexpsym; static int nimpsym; static char **dylib; static int ndylib; static vlong linkoff; int machowrite(void) { vlong o1; int loadsize; int i, j; MachoSeg *s; MachoSect *t; MachoDebug *d; MachoLoad *l; o1 = cpos(); loadsize = 4*4*ndebug; for(i=0; insect); strnput(s->name, 16); VPUT(s->vaddr); VPUT(s->vsize); VPUT(s->fileoffset); VPUT(s->filesize); LPUT(s->prot1); LPUT(s->prot2); LPUT(s->nsect); LPUT(s->flag); } else { LPUT(1); /* segment 32 */ LPUT(56+68*s->nsect); strnput(s->name, 16); LPUT(s->vaddr); LPUT(s->vsize); LPUT(s->fileoffset); LPUT(s->filesize); LPUT(s->prot1); LPUT(s->prot2); LPUT(s->nsect); LPUT(s->flag); } for(j=0; jnsect; j++) { t = &s->sect[j]; if(macho64) { strnput(t->name, 16); strnput(s->name, 16); VPUT(t->addr); VPUT(t->size); LPUT(t->off); LPUT(t->align); LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); LPUT(0); /* reserved */ LPUT(0); /* reserved */ LPUT(0); /* reserved */ } else { strnput(t->name, 16); strnput(s->name, 16); LPUT(t->addr); LPUT(t->size); LPUT(t->off); LPUT(t->align); LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); LPUT(0); /* reserved */ LPUT(0); /* reserved */ } } } for(i=0; itype); LPUT(4*(l->ndata+2)); for(j=0; jndata; j++) LPUT(l->data[j]); } for(i=0; ifileoffset); LPUT(d->filesize); } return cpos() - o1; } static void* grow(uchar **dat, uint32 *ndat, uint32 *mdat, uint32 n) { uchar *p; uint32 old; if(*ndat+n > *mdat) { old = *mdat; *mdat = (*ndat+n)*2 + 128; *dat = realloc(*dat, *mdat); if(*dat == 0) { diag("out of memory"); errorexit(); } memset(*dat+old, 0, *mdat-old); } p = *dat + *ndat; *ndat += n; return p; } static int needlib(char *name) { char *p; Sym *s; /* reuse hash code in symbol table */ p = smprint(".machoload.%s", name); s = lookup(p, 0); if(s->type == 0) { s->type = 100; // avoid SDATA, etc. return 1; } return 0; } void domacho(void) { int h, ptrsize, t; char *p; uchar *dat; uint32 x; Sym *s; Sym **impsym; ptrsize = 4; if(macho64) ptrsize = 8; // empirically, string table must begin with " \x00". if(!debug['d']) *(char*)grow(&strtab, &nstrtab, &mstrtab, 2) = ' '; impsym = nil; for(h=0; hlink) { if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) continue; if(debug['d']) { diag("cannot use dynamic loading and -d"); errorexit(); } if(!s->dynexport) { if(nimpsym%32 == 0) { impsym = realloc(impsym, (nimpsym+32)*sizeof impsym[0]); if(impsym == nil) { diag("out of memory"); errorexit(); } } impsym[nimpsym++] = s; continue; } /* symbol table entry - darwin still puts _ prefixes on all C symbols */ x = nstrtab; p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); *p++ = '_'; strcpy(p, s->dynimpname); dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); dat[0] = x; dat[1] = x>>8; dat[2] = x>>16; dat[3] = x>>24; dat[4] = 0x0f; // type: N_SECT | N_EXT - external, defined in sect switch(s->type) { default: case STEXT: t = 1; break; case SDATA: t = 2; break; case SBSS: t = 4; break; } dat[5] = t; // sect: section number if (nexpsym%32 == 0) { expsym = realloc(expsym, (nexpsym+32)*sizeof expsym[0]); if (expsym == nil) { diag("out of memory"); errorexit(); } } expsym[nexpsym].off = nlinkdata - ptrsize; expsym[nexpsym++].s = s; } } for(h=0; htype = SMACHO; s->value = (nexpsym+h) * ptrsize; /* symbol table entry - darwin still puts _ prefixes on all C symbols */ x = nstrtab; p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); *p++ = '_'; strcpy(p, s->dynimpname); dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); dat[0] = x; dat[1] = x>>8; dat[2] = x>>16; dat[3] = x>>24; dat[4] = 0x01; // type: N_EXT - external symbol if(needlib(s->dynimplib)) { if(ndylib%32 == 0) { dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); if(dylib == nil) { diag("out of memory"); errorexit(); } } dylib[ndylib++] = s->dynimplib; } } free(impsym); /* * list of symbol table indexes. * we don't take advantage of the opportunity * to order the symbol table differently from * this list, so it is boring: 0 1 2 3 4 ... */ for(x=0; x>8; dat[2] = x>>16; dat[3] = x>>24; } dynptrsize = (nexpsym+nimpsym) * ptrsize; } vlong domacholink(void) { int i; uchar *p; Sym *s; uint64 val; linkoff = 0; if(nlinkdata > 0) { linkoff = rnd(HEADR+textsize, INITRND) + rnd(datsize, INITRND); seek(cout, linkoff, 0); for(i = 0; ivalue; if(s->type == SUNDEF) diag("export of undefined symbol %s", s->name); if (s->type != STEXT) val += INITDAT; p = linkdata+expsym[i].off; p[0] = val; p[1] = val >> 8; p[2] = val >> 16; p[3] = val >> 24; if (macho64) { p[4] = val >> 32; p[5] = val >> 40; p[6] = val >> 48; p[7] = val >> 56; } } write(cout, linkdata, nlinkdata); write(cout, strtab, nstrtab); } return rnd(nlinkdata+nstrtab, INITRND); } void asmbmacho(vlong symdatva, vlong symo) { vlong v, w; vlong va; int a, i, ptrsize; char *pkgroot; MachoHdr *mh; MachoSect *msect; MachoSeg *ms; MachoDebug *md; MachoLoad *ml; /* apple MACH */ va = INITTEXT - HEADR; mh = getMachoHdr(); switch(thechar){ default: diag("unknown mach architecture"); errorexit(); case '6': mh->cpu = MACHO_CPU_AMD64; mh->subcpu = MACHO_SUBCPU_X86; ptrsize = 8; break; case '8': mh->cpu = MACHO_CPU_386; mh->subcpu = MACHO_SUBCPU_X86; ptrsize = 4; break; } /* segment for zero page */ ms = newMachoSeg("__PAGEZERO", 0); ms->vsize = va; /* text */ v = rnd(HEADR+textsize, INITRND); ms = newMachoSeg("__TEXT", 1); ms->vaddr = va; ms->vsize = v; ms->filesize = v; ms->prot1 = 7; ms->prot2 = 5; msect = newMachoSect(ms, "__text"); msect->addr = INITTEXT; msect->size = textsize; msect->off = INITTEXT - va; msect->flag = 0x400; /* flag - some instructions */ /* data */ w = datsize+dynptrsize+bsssize; ms = newMachoSeg("__DATA", 2+(dynptrsize>0)); ms->vaddr = va+v; ms->vsize = w; ms->fileoffset = v; ms->filesize = datsize; ms->prot1 = 7; ms->prot2 = 3; msect = newMachoSect(ms, "__data"); msect->addr = va+v; msect->size = datsize; msect->off = v; if(dynptrsize > 0) { msect = newMachoSect(ms, "__nl_symbol_ptr"); msect->addr = va+v+datsize; msect->size = dynptrsize; msect->align = 2; msect->flag = 6; /* section with nonlazy symbol pointers */ /* * The reserved1 field is supposed to be the index of * the first entry in the list of symbol table indexes * in isymtab for the symbols we need. We only use * pointers, so we need the entire list, so the index * here should be 0, which luckily is what the Mach-O * writing code emits by default for this not really reserved field. msect->reserved1 = 0; - first indirect symbol table entry we need */ } msect = newMachoSect(ms, "__bss"); msect->addr = va+v+datsize+dynptrsize; msect->size = bsssize; msect->flag = 1; /* flag - zero fill */ switch(thechar) { default: diag("unknown macho architecture"); errorexit(); case '6': ml = newMachoLoad(5, 42+2); /* unix thread */ ml->data[0] = 4; /* thread type */ ml->data[1] = 42; /* word count */ ml->data[2+32] = entryvalue(); /* start pc */ ml->data[2+32+1] = entryvalue()>>32; break; case '8': ml = newMachoLoad(5, 16+2); /* unix thread */ ml->data[0] = 1; /* thread type */ ml->data[1] = 16; /* word count */ ml->data[2+10] = entryvalue(); /* start pc */ break; } if(!debug['d']) { int nsym; nsym = dynptrsize/ptrsize; ms = newMachoSeg("__LINKEDIT", 0); ms->vaddr = va+v+rnd(datsize+dynptrsize+bsssize, INITRND); ms->vsize = nlinkdata+nstrtab; ms->fileoffset = linkoff; ms->filesize = nlinkdata+nstrtab; ms->prot1 = 7; ms->prot2 = 3; ml = newMachoLoad(2, 4); /* LC_SYMTAB */ ml->data[0] = linkoff; /* symoff */ ml->data[1] = nsym; /* nsyms */ ml->data[2] = linkoff + nlinkdata; /* stroff */ ml->data[3] = nstrtab; /* strsize */ ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ ml->data[0] = 0; /* ilocalsym */ ml->data[1] = 0; /* nlocalsym */ ml->data[2] = 0; /* iextdefsym */ ml->data[3] = nexpsym; /* nextdefsym */ ml->data[4] = nexpsym; /* iundefsym */ ml->data[5] = nimpsym; /* nundefsym */ ml->data[6] = 0; /* tocoffset */ ml->data[7] = 0; /* ntoc */ ml->data[8] = 0; /* modtaboff */ ml->data[9] = 0; /* nmodtab */ ml->data[10] = 0; /* extrefsymoff */ ml->data[11] = 0; /* nextrefsyms */ ml->data[12] = linkoff + nlinkdata - nsym*4; /* indirectsymoff */ ml->data[13] = nsym; /* nindirectsyms */ ml->data[14] = 0; /* extreloff */ ml->data[15] = 0; /* nextrel */ ml->data[16] = 0; /* locreloff */ ml->data[17] = 0; /* nlocrel */ ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */ ml->data[0] = 12; /* offset to string */ strcpy((char*)&ml->data[1], "/usr/lib/dyld"); if(ndylib > 0) { /* add reference to where .so files are installed */ pkgroot = smprint("%s/pkg/%s_%s", goroot, goos, goarch); ml = newMachoLoad(0x80000000 | 0x1c, 1+(strlen(pkgroot)+1+7)/8*2); /* LC_RPATH */ ml->data[0] = 12; /* offset of string from beginning of load */ strcpy((char*)&ml->data[1], pkgroot); } for(i=0; idata[0] = 24; /* offset of string from beginning of load */ ml->data[1] = 0; /* time stamp */ ml->data[2] = 0; /* version */ ml->data[3] = 0; /* compatibility version */ strcpy((char*)&ml->data[4], dylib[i]); } } if(!debug['s']) { ms = newMachoSeg("__SYMDAT", 1); ms->vaddr = symdatva; ms->vsize = 8+symsize+lcsize; ms->fileoffset = symo; ms->filesize = 8+symsize+lcsize; ms->prot1 = 7; ms->prot2 = 5; md = newMachoDebug(); md->fileoffset = symo+8; md->filesize = symsize; md = newMachoDebug(); md->fileoffset = symo+8+symsize; md->filesize = lcsize; dwarfaddmachoheaders(); } a = machowrite(); if(a > MACHORESERVE) diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE); }