diff options
Diffstat (limited to 'src/cmd/ld/macho.c')
-rw-r--r-- | src/cmd/ld/macho.c | 592 |
1 files changed, 406 insertions, 186 deletions
diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c index 6781c25a4..d135a92da 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -14,9 +14,20 @@ static int macho64; static MachoHdr hdr; static MachoLoad *load; static MachoSeg seg[16]; -static MachoDebug xdebug[16]; static int nload, mload, nseg, ndebug, nsect; +enum +{ + SymKindLocal = 0, + SymKindExtdef, + SymKindUndef, + NumSymKind +}; + +static int nkind[NumSymKind]; +static Sym** sortsym; +static int nsortsym; + // 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 @@ -25,6 +36,8 @@ static int nload, mload, nseg, ndebug, nsect; // up about 1300 bytes; we overestimate that as 2k. static int load_budget = INITIAL_MACHO_HEADR - 2*1024; +static void machodysymtab(void); + void machoinit(void) { @@ -56,11 +69,7 @@ newMachoLoad(uint32 type, uint32 ndata) mload = 1; else mload *= 2; - load = realloc(load, mload*sizeof load[0]); - if(load == nil) { - diag("out of memory"); - errorexit(); - } + load = erealloc(load, mload*sizeof load[0]); } if(macho64 && (ndata & 1)) @@ -90,7 +99,7 @@ newMachoSeg(char *name, int msect) } MachoSect* -newMachoSect(MachoSeg *seg, char *name) +newMachoSect(MachoSeg *seg, char *name, char *segname) { MachoSect *s; @@ -100,21 +109,11 @@ newMachoSect(MachoSeg *seg, char *name) } s = &seg->sect[seg->nsect++]; s->name = name; + s->segname = segname; nsect++; return s; } -MachoDebug* -newMachoDebug(void) -{ - if(ndebug >= nelem(xdebug)) { - diag("too many debugs"); - errorexit(); - } - return &xdebug[ndebug++]; -} - - // Generic linking code. static char **dylib; @@ -130,7 +129,6 @@ machowrite(void) int i, j; MachoSeg *s; MachoSect *t; - MachoDebug *d; MachoLoad *l; o1 = cpos(); @@ -152,7 +150,10 @@ machowrite(void) LPUT(0xfeedface); LPUT(hdr.cpu); LPUT(hdr.subcpu); - LPUT(2); /* file type - mach executable */ + if(linkmode == LinkExternal) + LPUT(1); /* file type - mach object */ + else + LPUT(2); /* file type - mach executable */ LPUT(nload+nseg+ndebug); LPUT(loadsize); LPUT(1); /* flags - no undefines */ @@ -190,7 +191,7 @@ machowrite(void) t = &s->sect[j]; if(macho64) { strnput(t->name, 16); - strnput(s->name, 16); + strnput(t->segname, 16); VPUT(t->addr); VPUT(t->size); LPUT(t->off); @@ -203,7 +204,7 @@ machowrite(void) LPUT(0); /* reserved */ } else { strnput(t->name, 16); - strnput(s->name, 16); + strnput(t->segname, 16); LPUT(t->addr); LPUT(t->size); LPUT(t->off); @@ -225,14 +226,6 @@ machowrite(void) 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; } @@ -245,31 +238,34 @@ domacho(void) return; // empirically, string table must begin with " \x00". - s = lookup(".dynstr", 0); - s->type = SMACHODYNSTR; + s = lookup(".machosymstr", 0); + s->type = SMACHOSYMSTR; s->reachable = 1; adduint8(s, ' '); adduint8(s, '\0'); - s = lookup(".dynsym", 0); - s->type = SMACHODYNSYM; + s = lookup(".machosymtab", 0); + s->type = SMACHOSYMTAB; s->reachable = 1; - s = lookup(".plt", 0); // will be __symbol_stub - s->type = SMACHOPLT; - s->reachable = 1; + if(linkmode != LinkExternal) { + 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(".got", 0); // will be __nl_symbol_ptr + s->type = SMACHOGOT; + s->reachable = 1; + s->align = 4; - s = lookup(".linkedit.plt", 0); // indirect table for .plt - s->type = SMACHOINDIRECTPLT; - 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; + s = lookup(".linkedit.got", 0); // indirect table for .got + s->type = SMACHOINDIRECTGOT; + s->reachable = 1; + } } void @@ -286,16 +282,62 @@ machoadddynlib(char *lib) load_budget += 4096; } - if(ndylib%32 == 0) { - dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); - if(dylib == nil) { - diag("out of memory"); - errorexit(); - } - } + if(ndylib%32 == 0) + dylib = erealloc(dylib, (ndylib+32)*sizeof dylib[0]); dylib[ndylib++] = lib; } +static void +machoshbits(MachoSeg *mseg, Section *sect, char *segname) +{ + MachoSect *msect; + char buf[40]; + char *p; + + snprint(buf, sizeof buf, "__%s", sect->name+1); + for(p=buf; *p; p++) + if(*p == '.') + *p = '_'; + + msect = newMachoSect(mseg, estrdup(buf), segname); + if(sect->rellen > 0) { + msect->reloc = sect->reloff; + msect->nreloc = sect->rellen / 8; + } + + while(1<<msect->align < sect->align) + msect->align++; + msect->addr = sect->vaddr; + msect->size = sect->len; + + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) { + // data in file + if(sect->len > sect->seg->vaddr + sect->seg->filelen - sect->vaddr) + diag("macho cannot represent section %s crossing data and bss", sect->name); + msect->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + } else { + // zero fill + msect->off = 0; + msect->flag |= 1; + } + + if(sect->rwx & 1) + msect->flag |= 0x400; /* has instructions */ + + if(strcmp(sect->name, ".plt") == 0) { + msect->name = "__symbol_stub1"; + msect->flag = 0x80000408; /* only instructions, code, symbol stubs */ + msect->res1 = 0;//nkind[SymKindLocal]; + msect->res2 = 6; + } + + if(strcmp(sect->name, ".got") == 0) { + msect->name = "__nl_symbol_ptr"; + msect->flag = 6; /* section with nonlazy symbol pointers */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ + } +} + void asmbmacho(void) { @@ -303,11 +345,9 @@ asmbmacho(void) vlong va; int a, i; MachoHdr *mh; - MachoSect *msect; MachoSeg *ms; - MachoDebug *md; MachoLoad *ml; - Sym *s; + Section *sect; /* apple MACH */ va = INITTEXT - HEADR; @@ -325,179 +365,305 @@ asmbmacho(void) mh->subcpu = MACHO_SUBCPU_X86; break; } + + ms = nil; + if(linkmode == LinkExternal) { + /* segment for entire file */ + ms = newMachoSeg("", 40); + ms->fileoffset = segtext.fileoff; + ms->filesize = segdata.fileoff + segdata.filelen - segtext.fileoff; + } /* segment for zero page */ - ms = newMachoSeg("__PAGEZERO", 0); - ms->vsize = va; + if(linkmode != LinkExternal) { + 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 */ + if(linkmode != LinkExternal) { + ms = newMachoSeg("__TEXT", 20); + ms->vaddr = va; + ms->vsize = v; + ms->fileoffset = 0; + ms->filesize = v; + ms->prot1 = 7; + ms->prot2 = 5; } + for(sect=segtext.sect; sect!=nil; sect=sect->next) + machoshbits(ms, sect, "__TEXT"); + /* 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 */ + if(linkmode != LinkExternal) { + w = segdata.len; + ms = newMachoSeg("__DATA", 20); + ms->vaddr = va+v; + ms->vsize = w; + ms->fileoffset = v; + ms->filesize = segdata.filelen; + ms->prot1 = 3; + ms->prot2 = 3; } - msect = newMachoSect(ms, "__bss"); - msect->addr = va+v+segdata.filelen; - msect->size = segdata.len - segdata.filelen; - msect->flag = 1; /* flag - zero fill */ + for(sect=segdata.sect; sect!=nil; sect=sect->next) + machoshbits(ms, sect, "__DATA"); - 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(linkmode != LinkExternal) { + 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); + s1 = lookup(".machosymtab", 0); s2 = lookup(".linkedit.plt", 0); s3 = lookup(".linkedit.got", 0); - s4 = lookup(".dynstr", 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; + s4 = lookup(".machosymstr", 0); + + if(linkmode != LinkExternal) { + 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[1] = nsortsym; /* nsyms */ ml->data[2] = linkoff + s1->size + s2->size + s3->size; /* stroff */ ml->data[3] = s4->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; /* indirectsymoff */ - ml->data[13] = (s2->size + s3->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]); + machodysymtab(); + + if(linkmode != LinkExternal) { + 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; - + // TODO: dwarf headers go in ms too + if(!debug['s'] && linkmode != LinkExternal) dwarfaddmachoheaders(); - } a = machowrite(); if(a > HEADR) diag("HEADR too small: %d > %d", a, HEADR); } +static int +symkind(Sym *s) +{ + if(s->type == SDYNIMPORT) + return SymKindUndef; + if(s->cgoexport) + return SymKindExtdef; + return SymKindLocal; +} + +static void +addsym(Sym *s, char *name, int type, vlong addr, vlong size, int ver, Sym *gotype) +{ + USED(name); + USED(addr); + USED(size); + USED(ver); + USED(gotype); + + if(s == nil) + return; + + switch(type) { + default: + return; + case 'D': + case 'B': + case 'T': + break; + } + + if(sortsym) { + sortsym[nsortsym] = s; + nkind[symkind(s)]++; + } + nsortsym++; +} + +static int +scmp(const void *p1, const void *p2) +{ + Sym *s1, *s2; + int k1, k2; + + s1 = *(Sym**)p1; + s2 = *(Sym**)p2; + + k1 = symkind(s1); + k2 = symkind(s2); + if(k1 != k2) + return k1 - k2; + + return strcmp(s1->extname, s2->extname); +} + +static void +machogenasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Sym *s; + + genasmsym(put); + for(s=allsym; s; s=s->allsym) + if(s->type == SDYNIMPORT || s->type == SHOSTOBJ) + if(s->reachable) + put(s, nil, 'D', 0, 0, 0, nil); +} + +void +machosymorder(void) +{ + int i; + + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See http://golang.org/issue/4029 + for(i=0; i<ndynexp; i++) + dynexp[i]->reachable = 1; + machogenasmsym(addsym); + sortsym = mal(nsortsym * sizeof sortsym[0]); + nsortsym = 0; + machogenasmsym(addsym); + qsort(sortsym, nsortsym, sizeof sortsym[0], scmp); + for(i=0; i<nsortsym; i++) + sortsym[i]->dynid = i; +} + +static void +machosymtab(void) +{ + int i; + Sym *symtab, *symstr, *s, *o; + + symtab = lookup(".machosymtab", 0); + symstr = lookup(".machosymstr", 0); + + for(i=0; i<nsortsym; i++) { + s = sortsym[i]; + adduint32(symtab, symstr->size); + + // Only add _ to C symbols. Go symbols have dot in the name. + if(strstr(s->extname, ".") == nil) + adduint8(symstr, '_'); + addstring(symstr, s->extname); + if(s->type == SDYNIMPORT || s->type == SHOSTOBJ) { + adduint8(symtab, 0x01); // type N_EXT, external symbol + adduint8(symtab, 0); // no section + adduint16(symtab, 0); // desc + adduintxx(symtab, 0, PtrSize); // no value + } else { + if(s->cgoexport) + adduint8(symtab, 0x0f); + else + adduint8(symtab, 0x0e); + o = s; + while(o->outer != nil) + o = o->outer; + if(o->sect == nil) { + diag("missing section for %s", s->name); + adduint8(symtab, 0); + } else + adduint8(symtab, o->sect->extnum); + adduint16(symtab, 0); // desc + adduintxx(symtab, symaddr(s), PtrSize); + } + } +} + +static void +machodysymtab(void) +{ + int n; + MachoLoad *ml; + Sym *s1, *s2, *s3; + + ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ + + n = 0; + ml->data[0] = n; /* ilocalsym */ + ml->data[1] = nkind[SymKindLocal]; /* nlocalsym */ + n += nkind[SymKindLocal]; + + ml->data[2] = n; /* iextdefsym */ + ml->data[3] = nkind[SymKindExtdef]; /* nextdefsym */ + n += nkind[SymKindExtdef]; + + ml->data[4] = n; /* iundefsym */ + ml->data[5] = nkind[SymKindUndef]; /* 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 */ + + // must match domacholink below + s1 = lookup(".machosymtab", 0); + s2 = lookup(".linkedit.plt", 0); + s3 = lookup(".linkedit.got", 0); + ml->data[12] = linkoff + s1->size; /* indirectsymoff */ + ml->data[13] = (s2->size + s3->size) / 4; /* nindirectsyms */ + + ml->data[14] = 0; /* extreloff */ + ml->data[15] = 0; /* nextrel */ + ml->data[16] = 0; /* locreloff */ + ml->data[17] = 0; /* nlocrel */ +} + vlong domacholink(void) { int size; Sym *s1, *s2, *s3, *s4; + machosymtab(); + // write data that will be linkedit section - s1 = lookup(".dynsym", 0); - relocsym(s1); + s1 = lookup(".machosymtab", 0); s2 = lookup(".linkedit.plt", 0); s3 = lookup(".linkedit.got", 0); - s4 = lookup(".dynstr", 0); + s4 = lookup(".machosymstr", 0); // Force the linkedit section to end on a 16-byte // boundary. This allows pure (non-cgo) Go binaries @@ -533,3 +699,57 @@ domacholink(void) return rnd(size, INITRND); } + + +void +machorelocsect(Section *sect, Sym *first) +{ + Sym *sym; + int32 eaddr; + Reloc *r; + + // If main section has no bits, nothing to relocate. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return; + + sect->reloff = cpos(); + for(sym = first; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= sect->vaddr) + break; + } + + eaddr = sect->vaddr + sect->len; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + cursym = sym; + + for(r = sym->r; r < sym->r+sym->nr; r++) { + if(r->done) + continue; + if(machoreloc1(r, sym->value+r->off - sect->vaddr) < 0) + diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); + } + } + + sect->rellen = cpos() - sect->reloff; +} + +void +machoemitreloc(void) +{ + Section *sect; + + while(cpos()&7) + cput(0); + + machorelocsect(segtext.sect, textp); + for(sect=segtext.sect->next; sect!=nil; sect=sect->next) + machorelocsect(sect, datap); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + machorelocsect(sect, datap); +} |