This source file includes following definitions.
- unpackcmd
- macholoadrel
- macholoaddsym
- macholoadsym
- ldmacho
#include "l.h"
#include "lib.h"
enum {
MACHO_FAKE_GOTPCREL = 100,
N_EXT = 0x01,
N_TYPE = 0x1e,
N_STAB = 0xe0,
};
typedef struct MachoObj MachoObj;
typedef struct MachoCmd MachoCmd;
typedef struct MachoSeg MachoSeg;
typedef struct MachoSect MachoSect;
typedef struct MachoRel MachoRel;
typedef struct MachoSymtab MachoSymtab;
typedef struct MachoSym MachoSym;
typedef struct MachoDysymtab MachoDysymtab;
enum
{
MachoCpuVax = 1,
MachoCpu68000 = 6,
MachoCpu386 = 7,
MachoCpuAmd64 = 0x1000007,
MachoCpuMips = 8,
MachoCpu98000 = 10,
MachoCpuHppa = 11,
MachoCpuArm = 12,
MachoCpu88000 = 13,
MachoCpuSparc = 14,
MachoCpu860 = 15,
MachoCpuAlpha = 16,
MachoCpuPower = 18,
MachoCmdSegment = 1,
MachoCmdSymtab = 2,
MachoCmdSymseg = 3,
MachoCmdThread = 4,
MachoCmdDysymtab = 11,
MachoCmdSegment64 = 25,
MachoFileObject = 1,
MachoFileExecutable = 2,
MachoFileFvmlib = 3,
MachoFileCore = 4,
MachoFilePreload = 5,
};
struct MachoSeg
{
char name[16+1];
uint64 vmaddr;
uint64 vmsize;
uint32 fileoff;
uint32 filesz;
uint32 maxprot;
uint32 initprot;
uint32 nsect;
uint32 flags;
MachoSect *sect;
};
struct MachoSect
{
char name[16+1];
char segname[16+1];
uint64 addr;
uint64 size;
uint32 off;
uint32 align;
uint32 reloff;
uint32 nreloc;
uint32 flags;
uint32 res1;
uint32 res2;
LSym *sym;
MachoRel *rel;
};
struct MachoRel
{
uint32 addr;
uint32 symnum;
uint8 pcrel;
uint8 length;
uint8 extrn;
uint8 type;
uint8 scattered;
uint32 value;
};
struct MachoSymtab
{
uint32 symoff;
uint32 nsym;
uint32 stroff;
uint32 strsize;
char *str;
MachoSym *sym;
};
struct MachoSym
{
char *name;
uint8 type;
uint8 sectnum;
uint16 desc;
char kind;
uint64 value;
LSym *sym;
};
struct MachoDysymtab
{
uint32 ilocalsym;
uint32 nlocalsym;
uint32 iextdefsym;
uint32 nextdefsym;
uint32 iundefsym;
uint32 nundefsym;
uint32 tocoff;
uint32 ntoc;
uint32 modtaboff;
uint32 nmodtab;
uint32 extrefsymoff;
uint32 nextrefsyms;
uint32 indirectsymoff;
uint32 nindirectsyms;
uint32 extreloff;
uint32 nextrel;
uint32 locreloff;
uint32 nlocrel;
uint32 *indir;
};
struct MachoCmd
{
int type;
uint32 off;
uint32 size;
MachoSeg seg;
MachoSymtab sym;
MachoDysymtab dsym;
};
struct MachoObj
{
Biobuf *f;
int64 base;
int64 len;
int is64;
char *name;
Endian *e;
uint cputype;
uint subcputype;
uint32 filetype;
uint32 flags;
MachoCmd *cmd;
uint ncmd;
};
static int
unpackcmd(uchar *p, MachoObj *m, MachoCmd *c, uint type, uint sz)
{
uint32 (*e4)(uchar*);
uint64 (*e8)(uchar*);
MachoSect *s;
int i;
e4 = m->e->e32;
e8 = m->e->e64;
c->type = type;
c->size = sz;
switch(type){
default:
return -1;
case MachoCmdSegment:
if(sz < 56)
return -1;
strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8);
c->seg.vmaddr = e4(p+24);
c->seg.vmsize = e4(p+28);
c->seg.fileoff = e4(p+32);
c->seg.filesz = e4(p+36);
c->seg.maxprot = e4(p+40);
c->seg.initprot = e4(p+44);
c->seg.nsect = e4(p+48);
c->seg.flags = e4(p+52);
c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]);
if(sz < 56+c->seg.nsect*68)
return -1;
p += 56;
for(i=0; i<c->seg.nsect; i++) {
s = &c->seg.sect[i];
strecpy(s->name, s->name+sizeof s->name, (char*)p+0);
strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16);
s->addr = e4(p+32);
s->size = e4(p+36);
s->off = e4(p+40);
s->align = e4(p+44);
s->reloff = e4(p+48);
s->nreloc = e4(p+52);
s->flags = e4(p+56);
s->res1 = e4(p+60);
s->res2 = e4(p+64);
p += 68;
}
break;
case MachoCmdSegment64:
if(sz < 72)
return -1;
strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8);
c->seg.vmaddr = e8(p+24);
c->seg.vmsize = e8(p+32);
c->seg.fileoff = e8(p+40);
c->seg.filesz = e8(p+48);
c->seg.maxprot = e4(p+56);
c->seg.initprot = e4(p+60);
c->seg.nsect = e4(p+64);
c->seg.flags = e4(p+68);
c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]);
if(sz < 72+c->seg.nsect*80)
return -1;
p += 72;
for(i=0; i<c->seg.nsect; i++) {
s = &c->seg.sect[i];
strecpy(s->name, s->name+sizeof s->name, (char*)p+0);
strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16);
s->addr = e8(p+32);
s->size = e8(p+40);
s->off = e4(p+48);
s->align = e4(p+52);
s->reloff = e4(p+56);
s->nreloc = e4(p+60);
s->flags = e4(p+64);
s->res1 = e4(p+68);
s->res2 = e4(p+72);
p += 80;
}
break;
case MachoCmdSymtab:
if(sz < 24)
return -1;
c->sym.symoff = e4(p+8);
c->sym.nsym = e4(p+12);
c->sym.stroff = e4(p+16);
c->sym.strsize = e4(p+20);
break;
case MachoCmdDysymtab:
if(sz < 80)
return -1;
c->dsym.ilocalsym = e4(p+8);
c->dsym.nlocalsym = e4(p+12);
c->dsym.iextdefsym = e4(p+16);
c->dsym.nextdefsym = e4(p+20);
c->dsym.iundefsym = e4(p+24);
c->dsym.nundefsym = e4(p+28);
c->dsym.tocoff = e4(p+32);
c->dsym.ntoc = e4(p+36);
c->dsym.modtaboff = e4(p+40);
c->dsym.nmodtab = e4(p+44);
c->dsym.extrefsymoff = e4(p+48);
c->dsym.nextrefsyms = e4(p+52);
c->dsym.indirectsymoff = e4(p+56);
c->dsym.nindirectsyms = e4(p+60);
c->dsym.extreloff = e4(p+64);
c->dsym.nextrel = e4(p+68);
c->dsym.locreloff = e4(p+72);
c->dsym.nlocrel = e4(p+76);
break;
}
return 0;
}
static int
macholoadrel(MachoObj *m, MachoSect *sect)
{
MachoRel *rel, *r;
uchar *buf, *p;
int i, n;
uint32 v;
if(sect->rel != nil || sect->nreloc == 0)
return 0;
rel = mal(sect->nreloc * sizeof r[0]);
n = sect->nreloc * 8;
buf = mal(n);
if(Bseek(m->f, m->base + sect->reloff, 0) < 0 || Bread(m->f, buf, n) != n)
return -1;
for(i=0; i<sect->nreloc; i++) {
r = &rel[i];
p = buf+i*8;
r->addr = m->e->e32(p);
if(r->addr & 0x80000000) {
r->scattered = 1;
v = r->addr >> 24;
r->addr &= 0xFFFFFF;
r->type = v & 0xF;
v >>= 4;
r->length = 1<<(v&3);
v >>= 2;
r->pcrel = v & 1;
r->value = m->e->e32(p+4);
} else {
v = m->e->e32(p+4);
r->symnum = v & 0xFFFFFF;
v >>= 24;
r->pcrel = v&1;
v >>= 1;
r->length = 1<<(v&3);
v >>= 2;
r->extrn = v&1;
v >>= 1;
r->type = v;
}
}
sect->rel = rel;
return 0;
}
static int
macholoaddsym(MachoObj *m, MachoDysymtab *d)
{
uchar *p;
int i, n;
n = d->nindirectsyms;
p = mal(n*4);
if(Bseek(m->f, m->base + d->indirectsymoff, 0) < 0 || Bread(m->f, p, n*4) != n*4)
return -1;
d->indir = (uint32*)p;
for(i=0; i<n; i++)
d->indir[i] = m->e->e32(p+4*i);
return 0;
}
static int
macholoadsym(MachoObj *m, MachoSymtab *symtab)
{
char *strbuf;
uchar *symbuf, *p;
int i, n, symsize;
MachoSym *sym, *s;
uint32 v;
if(symtab->sym != nil)
return 0;
strbuf = mal(symtab->strsize);
if(Bseek(m->f, m->base + symtab->stroff, 0) < 0 || Bread(m->f, strbuf, symtab->strsize) != symtab->strsize)
return -1;
symsize = 12;
if(m->is64)
symsize = 16;
n = symtab->nsym * symsize;
symbuf = mal(n);
if(Bseek(m->f, m->base + symtab->symoff, 0) < 0 || Bread(m->f, symbuf, n) != n)
return -1;
sym = mal(symtab->nsym * sizeof sym[0]);
p = symbuf;
for(i=0; i<symtab->nsym; i++) {
s = &sym[i];
v = m->e->e32(p);
if(v >= symtab->strsize)
return -1;
s->name = strbuf + v;
s->type = p[4];
s->sectnum = p[5];
s->desc = m->e->e16(p+6);
if(m->is64)
s->value = m->e->e64(p+8);
else
s->value = m->e->e32(p+8);
p += symsize;
}
symtab->str = strbuf;
symtab->sym = sym;
return 0;
}
void
ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
{
int i, j, is64;
uint64 secaddr;
uchar hdr[7*4], *cmdp;
uchar tmp[4];
uchar *dat;
ulong ncmd, cmdsz, ty, sz, off;
MachoObj *m;
Endian *e;
int64 base;
MachoSect *sect;
MachoRel *rel;
LSym *s, *s1, *outer;
MachoCmd *c;
MachoSymtab *symtab;
MachoDysymtab *dsymtab;
MachoSym *sym;
Reloc *r, *rp;
char *name;
ctxt->version++;
base = Boffset(f);
if(Bread(f, hdr, sizeof hdr) != sizeof hdr)
goto bad;
if((be.e32(hdr)&~1) == 0xFEEDFACE){
e = &be;
}else if((le.e32(hdr)&~1) == 0xFEEDFACE){
e = ≤
}else{
werrstr("bad magic - not mach-o file");
goto bad;
}
is64 = e->e32(hdr) == 0xFEEDFACF;
ncmd = e->e32(hdr+4*4);
cmdsz = e->e32(hdr+5*4);
if(ncmd > 0x10000 || cmdsz >= 0x01000000){
werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz);
goto bad;
}
if(is64)
Bread(f, tmp, 4);
m = mal(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz);
m->f = f;
m->e = e;
m->cputype = e->e32(hdr+1*4);
m->subcputype = e->e32(hdr+2*4);
m->filetype = e->e32(hdr+3*4);
m->ncmd = ncmd;
m->flags = e->e32(hdr+6*4);
m->is64 = is64;
m->base = base;
m->len = len;
m->name = pn;
switch(thechar) {
default:
diag("%s: mach-o %s unimplemented", pn, thestring);
return;
case '6':
if(e != &le || m->cputype != MachoCpuAmd64) {
diag("%s: mach-o object but not amd64", pn);
return;
}
break;
case '8':
if(e != &le || m->cputype != MachoCpu386) {
diag("%s: mach-o object but not 386", pn);
return;
}
break;
}
m->cmd = (MachoCmd*)(m+1);
off = sizeof hdr;
cmdp = (uchar*)(m->cmd+ncmd);
if(Bread(f, cmdp, cmdsz) != cmdsz){
werrstr("reading cmds: %r");
goto bad;
}
c = nil;
symtab = nil;
dsymtab = nil;
USED(dsymtab);
for(i=0; i<ncmd; i++){
ty = e->e32(cmdp);
sz = e->e32(cmdp+4);
m->cmd[i].off = off;
unpackcmd(cmdp, m, &m->cmd[i], ty, sz);
cmdp += sz;
off += sz;
if(ty == MachoCmdSymtab) {
if(symtab != nil) {
werrstr("multiple symbol tables");
goto bad;
}
symtab = &m->cmd[i].sym;
macholoadsym(m, symtab);
}
if(ty == MachoCmdDysymtab) {
dsymtab = &m->cmd[i].dsym;
macholoaddsym(m, dsymtab);
}
if((is64 && ty == MachoCmdSegment64) || (!is64 && ty == MachoCmdSegment)) {
if(c != nil) {
werrstr("multiple load commands");
goto bad;
}
c = &m->cmd[i];
}
}
if(c == nil) {
werrstr("no load command");
goto bad;
}
if(symtab == nil) {
return;
}
if(c->seg.fileoff+c->seg.filesz >= len) {
werrstr("load segment out of range");
goto bad;
}
dat = mal(c->seg.filesz);
if(Bseek(f, m->base + c->seg.fileoff, 0) < 0 || Bread(f, dat, c->seg.filesz) != c->seg.filesz) {
werrstr("cannot load object data: %r");
goto bad;
}
for(i=0; i<c->seg.nsect; i++) {
sect = &c->seg.sect[i];
if(strcmp(sect->segname, "__TEXT") != 0 && strcmp(sect->segname, "__DATA") != 0)
continue;
if(strcmp(sect->name, "__eh_frame") == 0)
continue;
name = smprint("%s(%s/%s)", pkg, sect->segname, sect->name);
s = linklookup(ctxt, name, ctxt->version);
if(s->type != 0) {
werrstr("duplicate %s/%s", sect->segname, sect->name);
goto bad;
}
free(name);
s->np = sect->size;
s->size = s->np;
if((sect->flags & 0xff) == 1)
s->p = mal(s->size);
else {
s->p = dat + sect->addr - c->seg.vmaddr;
}
if(strcmp(sect->segname, "__TEXT") == 0) {
if(strcmp(sect->name, "__text") == 0)
s->type = STEXT;
else
s->type = SRODATA;
} else {
if (strcmp(sect->name, "__bss") == 0) {
s->type = SBSS;
s->np = 0;
} else
s->type = SDATA;
}
sect->sym = s;
}
for(i=0; i<symtab->nsym; i++) {
int v;
sym = &symtab->sym[i];
if(sym->type&N_STAB)
continue;
name = sym->name;
if(name[0] == '_' && name[1] != '\0')
name++;
v = 0;
if(!(sym->type&N_EXT))
v = ctxt->version;
s = linklookup(ctxt, name, v);
if(!(sym->type&N_EXT))
s->dupok = 1;
sym->sym = s;
if(sym->sectnum == 0)
continue;
if(sym->sectnum > c->seg.nsect) {
werrstr("reference to invalid section %d", sym->sectnum);
goto bad;
}
sect = &c->seg.sect[sym->sectnum-1];
outer = sect->sym;
if(outer == nil) {
werrstr("reference to invalid section %s/%s", sect->segname, sect->name);
continue;
}
if(s->outer != S) {
if(s->dupok)
continue;
diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
errorexit();
}
s->type = outer->type | SSUB;
s->sub = outer->sub;
outer->sub = s;
s->outer = outer;
s->value = sym->value - sect->addr;
if(!(s->cgoexport & CgoExportDynamic))
s->dynimplib = nil;
if(outer->type == STEXT) {
if(s->external && !s->dupok)
diag("%s: duplicate definition of %s", pn, s->name);
s->external = 1;
}
sym->sym = s;
}
for(i=0; i<c->seg.nsect; i++) {
sect = &c->seg.sect[i];
if((s = sect->sym) == S)
continue;
if(s->sub) {
s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub));
for(s1 = s->sub; s1 != S; s1 = s1->sub) {
if(s1->sub)
s1->size = s1->sub->value - s1->value;
else
s1->size = s->value + s->size - s1->value;
}
}
if(s->type == STEXT) {
if(s->onlist)
sysfatal("symbol %s listed multiple times", s->name);
s->onlist = 1;
if(ctxt->etextp)
ctxt->etextp->next = s;
else
ctxt->textp = s;
ctxt->etextp = s;
for(s1 = s->sub; s1 != S; s1 = s1->sub) {
if(s1->onlist)
sysfatal("symbol %s listed multiple times", s1->name);
s1->onlist = 1;
ctxt->etextp->next = s1;
ctxt->etextp = s1;
}
}
}
for(i=0; i<c->seg.nsect; i++) {
sect = &c->seg.sect[i];
if((s = sect->sym) == S)
continue;
macholoadrel(m, sect);
if(sect->rel == nil)
continue;
r = mal(sect->nreloc*sizeof r[0]);
rp = r;
rel = sect->rel;
for(j=0; j<sect->nreloc; j++, rel++) {
if(rel->scattered) {
int k;
MachoSect *ks;
if(thechar != '8') {
diag("unexpected scattered relocation");
continue;
}
if(j+1 >= sect->nreloc) {
werrstr("unsupported scattered relocation %d", (int)rel->type);
goto bad;
}
if(!(rel+1)->scattered || (rel+1)->type != 1 ||
(rel->type != 4 && rel->type != 2) ||
(rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) {
werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type);
goto bad;
}
rp->siz = rel->length;
rp->off = rel->addr;
rp->add = 0;
rp->type = R_PCREL;
rp->add += (rp->off+4) - ((rel+1)->value - sect->addr);
for(k=0; k<c->seg.nsect; k++) {
ks = &c->seg.sect[k];
if(ks->addr <= rel->value && rel->value < ks->addr+ks->size)
goto foundk;
}
werrstr("unsupported scattered relocation: invalid address %#ux", rel->addr);
goto bad;
foundk:
if(ks->sym != S) {
rp->sym = ks->sym;
rp->add += rel->value - ks->addr;
} else if(strcmp(ks->segname, "__IMPORT") == 0 && strcmp(ks->name, "__pointers") == 0) {
rp->type = 512 + MACHO_FAKE_GOTPCREL;
k = ks->res1 + (rel->value - ks->addr) / 4;
if(dsymtab == nil || k < 0 || k >= dsymtab->nindirectsyms || dsymtab->indir == nil) {
werrstr("invalid scattered relocation: indirect symbol reference out of range");
goto bad;
}
k = dsymtab->indir[k];
if(k < 0 || k >= symtab->nsym) {
werrstr("invalid scattered relocation: symbol reference out of range");
goto bad;
}
rp->sym = symtab->sym[k].sym;
} else {
werrstr("unsupported scattered relocation: reference to %s/%s", ks->segname, ks->name);
goto bad;
}
rp++;
rel++;
j++;
continue;
}
rp->siz = rel->length;
rp->type = 512 + (rel->type<<1) + rel->pcrel;
rp->off = rel->addr;
if (thechar == '6' && rel->extrn == 0 && rel->type == 1) {
secaddr = c->seg.sect[rel->symnum-1].addr;
rp->add = (int32)e->e32(s->p+rp->off) + rp->off + 4 - secaddr;
} else
rp->add = (int32)e->e32(s->p+rp->off);
if(rel->pcrel && thechar == '8')
rp->add += rp->off+rp->siz;
if(!rel->extrn) {
if(rel->symnum < 1 || rel->symnum > c->seg.nsect) {
werrstr("invalid relocation: section reference out of range %d vs %d", rel->symnum, c->seg.nsect);
goto bad;
}
rp->sym = c->seg.sect[rel->symnum-1].sym;
if(rp->sym == nil) {
werrstr("invalid relocation: %s", c->seg.sect[rel->symnum-1].name);
goto bad;
}
if(thechar == '8')
rp->add -= c->seg.sect[rel->symnum-1].addr;
} else {
if(rel->symnum >= symtab->nsym) {
werrstr("invalid relocation: symbol reference out of range");
goto bad;
}
rp->sym = symtab->sym[rel->symnum].sym;
}
rp++;
}
qsort(r, rp - r, sizeof r[0], rbyoff);
s->r = r;
s->nr = rp - r;
}
return;
bad:
diag("%s: malformed mach-o file: %r", pn);
}