summaryrefslogtreecommitdiff
path: root/src/cmd/ld/macho.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ld/macho.c')
-rw-r--r--src/cmd/ld/macho.c518
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);
+}