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);    
}