| // 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/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 = Boffset(&bso); | |
| loadsize = 4*4*ndebug; | |
| for(i=0; i<nload; i++) | |
| loadsize += 4*(load[i].ndata+2); | |
| if(macho64) { | |
| loadsize += 18*4*nseg; | |
| loadsize += 20*4*nsect; | |
| } else { | |
| loadsize += 14*4*nseg; | |
| loadsize += 17*4*nsect; | |
| } | |
| if(macho64) | |
| LPUT(0xfeedfacf); | |
| else | |
| LPUT(0xfeedface); | |
| LPUT(hdr.cpu); | |
| LPUT(hdr.subcpu); | |
| LPUT(2); /* file type - mach executable */ | |
| LPUT(nload+nseg+ndebug); | |
| LPUT(loadsize); | |
| LPUT(1); /* flags - no undefines */ | |
| if(macho64) | |
| LPUT(0); /* reserved */ | |
| for(i=0; i<nseg; i++) { | |
| s = &seg[i]; | |
| if(macho64) { | |
| LPUT(25); /* segment 64 */ | |
| LPUT(72+80*s->nsect); | |
| 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; j<s->nsect; 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; i<nload; i++) { | |
| l = &load[i]; | |
| LPUT(l->type); | |
| LPUT(4*(l->ndata+2)); | |
| for(j=0; j<l->ndata; j++) | |
| LPUT(l->data[j]); | |
| } | |
| for(i=0; i<ndebug; i++) { | |
| d = &xdebug[i]; | |
| LPUT(3); /* obsolete gdb debug info */ | |
| LPUT(16); /* size of symseg command */ | |
| LPUT(d->fileoffset); | |
| LPUT(d->filesize); | |
| } | |
| return Boffset(&bso) - 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; h<NHASH; h++) { | |
| for(s=hash[h]; s!=S; s=s->link) { | |
| 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; h<nimpsym; h++) { | |
| s = impsym[h]; | |
| s->type = 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<nexpsym+nimpsym; x++) { | |
| dat = grow(&linkdata, &nlinkdata, &mlinkdata, 4); | |
| dat[0] = x; | |
| dat[1] = 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; i<nexpsym; ++i) { | |
| s = expsym[i].s; | |
| val = s->value; | |
| 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; | |
| 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"); | |
| for(i=0; i<ndylib; i++) { | |
| ml = newMachoLoad(12, 4+(strlen(dylib[i])+1+7)/8*2); /* LC_LOAD_DYLIB */ | |
| ml->data[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; | |
| } | |
| a = machowrite(); | |
| if(a > MACHORESERVE) | |
| diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE); | |
| } |