diff options
Diffstat (limited to 'src/cmd/ld/macho.c')
-rw-r--r-- | src/cmd/ld/macho.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c new file mode 100644 index 000000000..70133d665 --- /dev/null +++ b/src/cmd/ld/macho.c @@ -0,0 +1,518 @@ +// 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; +static MachoSeg seg[16]; +static MachoDebug xdebug[16]; +static int nload, mload, nseg, ndebug, nsect; + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +static int load_budget = INITIAL_MACHO_HEADR - 2*1024; + +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 >= mload) { + if(mload == 0) + mload = 1; + else + mload *= 2; + load = realloc(load, mload*sizeof load[0]); + if(load == nil) { + diag("out of memory"); + errorexit(); + } + } + + if(macho64 && (ndata & 1)) + ndata++; + + 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 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; 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(t->res1); /* reserved */ + LPUT(t->res2); /* 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(t->res1); /* reserved */ + LPUT(t->res2); /* 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 cpos() - o1; +} + +void +domacho(void) +{ + Sym *s; + + if(debug['d']) + return; + + // empirically, string table must begin with " \x00". + s = lookup(".dynstr", 0); + s->type = SMACHODYNSTR; + s->reachable = 1; + adduint8(s, ' '); + adduint8(s, '\0'); + + s = lookup(".dynsym", 0); + s->type = SMACHODYNSYM; + s->reachable = 1; + + s = lookup(".plt", 0); // will be __symbol_stub + s->type = SMACHOPLT; + s->reachable = 1; + + s = lookup(".got", 0); // will be __nl_symbol_ptr + s->type = SMACHOGOT; + s->reachable = 1; + + s = lookup(".linkedit.plt", 0); // indirect table for .plt + s->type = SMACHOINDIRECTPLT; + s->reachable = 1; + + s = lookup(".linkedit.got", 0); // indirect table for .got + s->type = SMACHOINDIRECTGOT; + s->reachable = 1; +} + +void +machoadddynlib(char *lib) +{ + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + load_budget -= (strlen(lib)+7)/8*8 + 24; + if(load_budget < 0) { + HEADR += 4096; + INITTEXT += 4096; + load_budget += 4096; + } + + if(ndylib%32 == 0) { + dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); + if(dylib == nil) { + diag("out of memory"); + errorexit(); + } + } + dylib[ndylib++] = lib; +} + +void +asmbmacho(void) +{ + vlong v, w; + vlong va; + int a, i; + MachoHdr *mh; + MachoSect *msect; + MachoSeg *ms; + MachoDebug *md; + MachoLoad *ml; + Sym *s; + + /* 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; + break; + case '8': + mh->cpu = MACHO_CPU_386; + mh->subcpu = MACHO_SUBCPU_X86; + break; + } + + /* segment for zero page */ + ms = newMachoSeg("__PAGEZERO", 0); + ms->vsize = va; + + /* text */ + v = rnd(HEADR+segtext.len, INITRND); + ms = newMachoSeg("__TEXT", 2); + ms->vaddr = va; + ms->vsize = v; + ms->filesize = v; + ms->prot1 = 7; + ms->prot2 = 5; + + msect = newMachoSect(ms, "__text"); + msect->addr = INITTEXT; + msect->size = segtext.sect->len; + msect->off = INITTEXT - va; + msect->flag = 0x400; /* flag - some instructions */ + + s = lookup(".plt", 0); + if(s->size > 0) { + msect = newMachoSect(ms, "__symbol_stub1"); + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = ms->fileoffset + msect->addr - ms->vaddr; + msect->flag = 0x80000408; /* flag */ + msect->res1 = 0; /* index into indirect symbol table */ + msect->res2 = 6; /* size of stubs */ + } + + /* data */ + w = segdata.len; + ms = newMachoSeg("__DATA", 3); + ms->vaddr = va+v; + ms->vsize = w; + ms->fileoffset = v; + ms->filesize = segdata.filelen; + ms->prot1 = 7; + ms->prot2 = 3; + + msect = newMachoSect(ms, "__data"); + msect->addr = va+v; + msect->off = v; + msect->size = segdata.filelen; + + s = lookup(".got", 0); + if(s->size > 0) { + msect->size = symaddr(s) - msect->addr; + + msect = newMachoSect(ms, "__nl_symbol_ptr"); + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = datoff(msect->addr); + msect->align = 2; + msect->flag = 6; /* section with nonlazy symbol pointers */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ + } + + msect = newMachoSect(ms, "__bss"); + msect->addr = va+v+segdata.filelen; + msect->size = segdata.len - segdata.filelen; + 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()>>16>>16; // hide >>32 for 8l + 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']) { + Sym *s1, *s2, *s3, *s4; + + // must match domacholink below + s1 = lookup(".dynsym", 0); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); + + ms = newMachoSeg("__LINKEDIT", 0); + ms->vaddr = va+v+rnd(segdata.len, INITRND); + ms->vsize = s1->size + s2->size + s3->size + s4->size; + ms->fileoffset = linkoff; + ms->filesize = ms->vsize; + ms->prot1 = 7; + ms->prot2 = 3; + + ml = newMachoLoad(2, 4); /* LC_SYMTAB */ + ml->data[0] = linkoff; /* symoff */ + ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */ + ml->data[2] = linkoff + s1->size; /* stroff */ + ml->data[3] = s2->size; /* 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] = ndynexp; /* nextdefsym */ + ml->data[4] = ndynexp; /* iundefsym */ + ml->data[5] = (s1->size / (macho64 ? 16 : 12)) - ndynexp; /* 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 + s1->size + s2->size; /* indirectsymoff */ + ml->data[13] = (s3->size + s4->size) / 4; /* 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']) { + Sym *s; + + md = newMachoDebug(); + s = lookup("symtab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + md = newMachoDebug(); + s = lookup("pclntab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + dwarfaddmachoheaders(); + } + + a = machowrite(); + if(a > HEADR) + diag("HEADR too small: %d > %d", a, HEADR); +} + +vlong +domacholink(void) +{ + int size; + Sym *s1, *s2, *s3, *s4; + + // write data that will be linkedit section + s1 = lookup(".dynsym", 0); + relocsym(s1); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); + + while(s2->size%4) + adduint8(s2, 0); + + size = s1->size + s2->size + s3->size + s4->size; + + if(size > 0) { + linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + cseek(linkoff); + + cwrite(s1->p, s1->size); + cwrite(s2->p, s2->size); + cwrite(s3->p, s3->size); + cwrite(s4->p, s4->size); + } + + return rnd(size, INITRND); +} |