This source file includes following definitions.
- machoinit
- getMachoHdr
- newMachoLoad
- newMachoSeg
- newMachoSect
- machowrite
- domacho
- machoadddynlib
- machoshbits
- asmbmacho
- symkind
- addsym
- scmp
- machogenasmsym
- machosymorder
- machosymtab
- machodysymtab
- domacholink
- machorelocsect
- machoemitreloc
#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 int nload, mload, nseg, ndebug, nsect;
enum
{
SymKindLocal = 0,
SymKindExtdef,
SymKindUndef,
NumSymKind
};
static int nkind[NumSymKind];
static LSym** sortsym;
static int nsortsym;
static int load_budget = INITIAL_MACHO_HEADR - 2*1024;
static void machodysymtab(void);
void
machoinit(void)
{
switch(thechar) {
case '6':
macho64 = 1;
break;
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 = erealloc(load, mload*sizeof load[0]);
}
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, char *segname)
{
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;
s->segname = segname;
nsect++;
return s;
}
static char **dylib;
static int ndylib;
static vlong linkoff;
int
machowrite(void)
{
vlong o1;
int loadsize;
int i, j;
MachoSeg *s;
MachoSect *t;
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);
if(linkmode == LinkExternal)
LPUT(1);
else
LPUT(2);
LPUT(nload+nseg+ndebug);
LPUT(loadsize);
LPUT(1);
if(macho64)
LPUT(0);
for(i=0; i<nseg; i++) {
s = &seg[i];
if(macho64) {
LPUT(25);
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);
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(t->segname, 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);
LPUT(t->res2);
LPUT(0);
} else {
strnput(t->name, 16);
strnput(t->segname, 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);
LPUT(t->res2);
}
}
}
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]);
}
return cpos() - o1;
}
void
domacho(void)
{
LSym *s;
if(debug['d'])
return;
s = linklookup(ctxt, ".machosymstr", 0);
s->type = SMACHOSYMSTR;
s->reachable = 1;
adduint8(ctxt, s, ' ');
adduint8(ctxt, s, '\0');
s = linklookup(ctxt, ".machosymtab", 0);
s->type = SMACHOSYMTAB;
s->reachable = 1;
if(linkmode != LinkExternal) {
s = linklookup(ctxt, ".plt", 0);
s->type = SMACHOPLT;
s->reachable = 1;
s = linklookup(ctxt, ".got", 0);
s->type = SMACHOGOT;
s->reachable = 1;
s->align = 4;
s = linklookup(ctxt, ".linkedit.plt", 0);
s->type = SMACHOINDIRECTPLT;
s->reachable = 1;
s = linklookup(ctxt, ".linkedit.got", 0);
s->type = SMACHOINDIRECTGOT;
s->reachable = 1;
}
}
void
machoadddynlib(char *lib)
{
load_budget -= (strlen(lib)+7)/8*8 + 24;
if(load_budget < 0) {
HEADR += 4096;
INITTEXT += 4096;
load_budget += 4096;
}
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) {
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 {
msect->off = 0;
msect->flag |= 1;
}
if(sect->rwx & 1)
msect->flag |= 0x400;
if(strcmp(sect->name, ".plt") == 0) {
msect->name = "__symbol_stub1";
msect->flag = 0x80000408;
msect->res1 = 0;
msect->res2 = 6;
}
if(strcmp(sect->name, ".got") == 0) {
msect->name = "__nl_symbol_ptr";
msect->flag = 6;
msect->res1 = linklookup(ctxt, ".linkedit.plt", 0)->size / 4;
}
}
void
asmbmacho(void)
{
vlong v, w;
vlong va;
int a, i;
MachoHdr *mh;
MachoSeg *ms;
MachoLoad *ml;
Section *sect;
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;
}
ms = nil;
if(linkmode == LinkExternal) {
ms = newMachoSeg("", 40);
ms->fileoffset = segtext.fileoff;
ms->filesize = segdata.fileoff + segdata.filelen - segtext.fileoff;
}
if(linkmode != LinkExternal) {
ms = newMachoSeg("__PAGEZERO", 0);
ms->vsize = va;
}
v = rnd(HEADR+segtext.len, INITRND);
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");
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;
}
for(sect=segdata.sect; sect!=nil; sect=sect->next)
machoshbits(ms, sect, "__DATA");
if(linkmode != LinkExternal) {
switch(thechar) {
default:
diag("unknown macho architecture");
errorexit();
case '6':
ml = newMachoLoad(5, 42+2);
ml->data[0] = 4;
ml->data[1] = 42;
ml->data[2+32] = entryvalue();
ml->data[2+32+1] = entryvalue()>>16>>16;
break;
case '8':
ml = newMachoLoad(5, 16+2);
ml->data[0] = 1;
ml->data[1] = 16;
ml->data[2+10] = entryvalue();
break;
}
}
if(!debug['d']) {
LSym *s1, *s2, *s3, *s4;
s1 = linklookup(ctxt, ".machosymtab", 0);
s2 = linklookup(ctxt, ".linkedit.plt", 0);
s3 = linklookup(ctxt, ".linkedit.got", 0);
s4 = linklookup(ctxt, ".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);
ml->data[0] = linkoff;
ml->data[1] = nsortsym;
ml->data[2] = linkoff + s1->size + s2->size + s3->size;
ml->data[3] = s4->size;
machodysymtab();
if(linkmode != LinkExternal) {
ml = newMachoLoad(14, 6);
ml->data[0] = 12;
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);
ml->data[0] = 24;
ml->data[1] = 0;
ml->data[2] = 0;
ml->data[3] = 0;
strcpy((char*)&ml->data[4], dylib[i]);
}
}
}
if(!debug['s'] && linkmode != LinkExternal)
dwarfaddmachoheaders();
a = machowrite();
if(a > HEADR)
diag("HEADR too small: %d > %d", a, HEADR);
}
static int
symkind(LSym *s)
{
if(s->type == SDYNIMPORT)
return SymKindUndef;
if(s->cgoexport)
return SymKindExtdef;
return SymKindLocal;
}
static void
addsym(LSym *s, char *name, int type, vlong addr, vlong size, int ver, LSym *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)
{
LSym *s1, *s2;
int k1, k2;
s1 = *(LSym**)p1;
s2 = *(LSym**)p2;
k1 = symkind(s1);
k2 = symkind(s2);
if(k1 != k2)
return k1 - k2;
return strcmp(s1->extname, s2->extname);
}
static void
machogenasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*))
{
LSym *s;
genasmsym(put);
for(s=ctxt->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;
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;
LSym *symtab, *symstr, *s, *o;
char *p;
symtab = linklookup(ctxt, ".machosymtab", 0);
symstr = linklookup(ctxt, ".machosymstr", 0);
for(i=0; i<nsortsym; i++) {
s = sortsym[i];
adduint32(ctxt, symtab, symstr->size);
if(strstr(s->extname, ".") == nil)
adduint8(ctxt, symstr, '_');
if(strstr(s->extname, "·") == nil) {
addstring(symstr, s->extname);
} else {
p = s->extname;
while (*p++ != '\0') {
if((uchar)*p == 0xc2 && (uchar)*(p+1) == 0xb7) {
adduint8(ctxt, symstr, '.');
p++;
} else {
adduint8(ctxt, symstr, *p);
}
}
adduint8(ctxt, symstr, '\0');
}
if(s->type == SDYNIMPORT || s->type == SHOSTOBJ) {
adduint8(ctxt, symtab, 0x01);
adduint8(ctxt, symtab, 0);
adduint16(ctxt, symtab, 0);
adduintxx(ctxt, symtab, 0, PtrSize);
} else {
if(s->cgoexport)
adduint8(ctxt, symtab, 0x0f);
else
adduint8(ctxt, symtab, 0x0e);
o = s;
while(o->outer != nil)
o = o->outer;
if(o->sect == nil) {
diag("missing section for %s", s->name);
adduint8(ctxt, symtab, 0);
} else
adduint8(ctxt, symtab, o->sect->extnum);
adduint16(ctxt, symtab, 0);
adduintxx(ctxt, symtab, symaddr(s), PtrSize);
}
}
}
static void
machodysymtab(void)
{
int n;
MachoLoad *ml;
LSym *s1, *s2, *s3;
ml = newMachoLoad(11, 18);
n = 0;
ml->data[0] = n;
ml->data[1] = nkind[SymKindLocal];
n += nkind[SymKindLocal];
ml->data[2] = n;
ml->data[3] = nkind[SymKindExtdef];
n += nkind[SymKindExtdef];
ml->data[4] = n;
ml->data[5] = nkind[SymKindUndef];
ml->data[6] = 0;
ml->data[7] = 0;
ml->data[8] = 0;
ml->data[9] = 0;
ml->data[10] = 0;
ml->data[11] = 0;
s1 = linklookup(ctxt, ".machosymtab", 0);
s2 = linklookup(ctxt, ".linkedit.plt", 0);
s3 = linklookup(ctxt, ".linkedit.got", 0);
ml->data[12] = linkoff + s1->size;
ml->data[13] = (s2->size + s3->size) / 4;
ml->data[14] = 0;
ml->data[15] = 0;
ml->data[16] = 0;
ml->data[17] = 0;
}
vlong
domacholink(void)
{
int size;
LSym *s1, *s2, *s3, *s4;
machosymtab();
s1 = linklookup(ctxt, ".machosymtab", 0);
s2 = linklookup(ctxt, ".linkedit.plt", 0);
s3 = linklookup(ctxt, ".linkedit.got", 0);
s4 = linklookup(ctxt, ".machosymstr", 0);
while(s4->size%16)
adduint8(ctxt, s4, 0);
size = s1->size + s2->size + s3->size + s4->size;
if(size > 0) {
linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND) + rnd(segdwarf.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);
}
void
machorelocsect(Section *sect, LSym *first)
{
LSym *sym;
int32 eaddr;
Reloc *r;
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;
ctxt->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, ctxt->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);
}