libmach: more mach-o support

R=rsc
http://codereview.appspot.com/2277041
This commit is contained in:
Russ Cox 2010-09-24 12:25:25 -04:00
parent 875351f44f
commit 873e5f5094
5 changed files with 343 additions and 35 deletions

View file

@ -11,6 +11,7 @@ file such as NOTICE, LICENCE or COPYING.
Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others. Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
Portions Copyright © 2001-2007 Russ Cox. Portions Copyright © 2001-2007 Russ Cox.
Portions Copyright © 2008-2010 Google Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -115,8 +115,8 @@ crackmacho(int fd, Fhdr *fp)
if(m->cmd[i].type == MachoCmdSymtab) if(m->cmd[i].type == MachoCmdSymtab)
break; break;
if(i < m->ncmd){ if(i < m->ncmd){
fp->stabs.stabbase = load(fp->fd, m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms*16); fp->stabs.stabbase = load(fp->fd, m->cmd[i].sym.symoff, m->cmd[i].sym.nsym*16);
fp->stabs.stabsize = m->cmd[i].sym.nsyms*16; fp->stabs.stabsize = m->cmd[i].sym.nsym*16;
fp->stabs.strbase = (char*)load(fp->fd, m->cmd[i].sym.stroff, m->cmd[i].sym.strsize); fp->stabs.strbase = (char*)load(fp->fd, m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
if(fp->stabs.stabbase == nil || fp->stabs.strbase == nil){ if(fp->stabs.stabbase == nil || fp->stabs.strbase == nil){
fp->stabs.stabbase = nil; fp->stabs.stabbase = nil;

View file

@ -6,6 +6,27 @@
/* /*
http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/ http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/
*/ */
static long
preadn(int fd, void *vdata, uint32 ulen, uint64 offset)
{
long n;
uchar *data;
long len;
len = ulen;
data = vdata;
/* fprint(2, "readn 0x%llux 0x%ux\n", offset, ulen); */
while(len > 0){
n = pread(fd, data, len, offset);
if(n <= 0)
break;
data += n;
offset += n;
len -= n;
}
return data-(uchar*)vdata;
}
Macho* Macho*
machoopen(char *name) machoopen(char *name)
@ -22,11 +43,15 @@ machoopen(char *name)
} }
static int static int
unpackseg(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz) unpackcmd(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz)
{ {
u32int (*e4)(uchar*); uint32 (*e4)(uchar*);
uint64 (*e8)(uchar*);
MachoSect *s;
int i;
e4 = m->e4; e4 = m->e4;
e8 = m->e8;
c->type = type; c->type = type;
c->size = sz; c->size = sz;
@ -45,59 +70,257 @@ unpackseg(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz)
c->seg.initprot = e4(p+44); c->seg.initprot = e4(p+44);
c->seg.nsect = e4(p+48); c->seg.nsect = e4(p+48);
c->seg.flags = e4(p+52); c->seg.flags = e4(p+52);
c->seg.sect = mallocz(c->seg.nsect * sizeof c->seg.sect[0], 1);
if(c->seg.sect == nil)
return -1;
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->offset = e4(p+40);
s->align = e4(p+44);
s->reloff = e4(p+48);
s->nreloc = e4(p+52);
s->flags = e4(p+56);
// p+60 and p+64 are reserved
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 = mallocz(c->seg.nsect * sizeof c->seg.sect[0], 1);
if(c->seg.sect == nil)
return -1;
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->offset = e4(p+48);
s->align = e4(p+52);
s->reloff = e4(p+56);
s->nreloc = e4(p+60);
s->flags = e4(p+64);
// p+68, p+72, and p+76 are reserved
p += 80;
}
break; break;
case MachoCmdSymtab: case MachoCmdSymtab:
if(sz < 24) if(sz < 24)
return -1; return -1;
c->sym.symoff = e4(p+8); c->sym.symoff = e4(p+8);
c->sym.nsyms = e4(p+12); c->sym.nsym = e4(p+12);
c->sym.stroff = e4(p+16); c->sym.stroff = e4(p+16);
c->sym.strsize = e4(p+20); c->sym.strsize = e4(p+20);
break; 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; return 0;
} }
int
macholoadrel(Macho *m, MachoSect *sect)
{
MachoRel *rel, *r;
uchar *buf, *p;
int i, n;
uint32 v;
if(sect->rel != nil || sect->nreloc == 0)
return 0;
rel = mallocz(sect->nreloc * sizeof r[0], 1);
if(rel == nil)
return -1;
n = sect->nreloc * 8;
buf = mallocz(n, 1);
if(buf == nil) {
free(rel);
return -1;
}
if(seek(m->fd, sect->reloff, 0) < 0 || readn(m->fd, buf, n) != n) {
free(rel);
free(buf);
return -1;
}
for(i=0; i<sect->nreloc; i++) {
r = &rel[i];
p = buf+i*8;
r->addr = m->e4(p);
// TODO(rsc): Wrong interpretation for big-endian bitfields?
v = m->e4(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;
free(buf);
return 0;
}
int
macholoadsym(Macho *m, MachoSymtab *symtab)
{
char *strbuf;
uchar *symbuf, *p;
int i, n, symsize;
MachoSym *sym, *s;
uint32 v;
if(symtab->sym != nil)
return 0;
strbuf = mallocz(symtab->strsize, 1);
if(strbuf == nil)
return -1;
if(seek(m->fd, symtab->stroff, 0) < 0 || readn(m->fd, strbuf, symtab->strsize) != symtab->strsize) {
free(strbuf);
return -1;
}
symsize = 12;
if(m->is64)
symsize = 16;
n = symtab->nsym * symsize;
symbuf = mallocz(n, 1);
if(symbuf == nil) {
free(strbuf);
return -1;
}
if(seek(m->fd, symtab->symoff, 0) < 0 || readn(m->fd, symbuf, n) != n) {
free(strbuf);
free(symbuf);
return -1;
}
sym = mallocz(symtab->nsym * sizeof sym[0], 1);
if(sym == nil) {
free(strbuf);
free(symbuf);
return -1;
}
p = symbuf;
for(i=0; i<symtab->nsym; i++) {
s = &sym[i];
v = m->e4(p);
if(v >= symtab->strsize) {
free(strbuf);
free(symbuf);
free(sym);
return -1;
}
s->name = strbuf + v;
s->type = p[4];
s->sectnum = p[5];
s->desc = m->e2(p+6);
if(m->is64)
s->value = m->e8(p+8);
else
s->value = m->e4(p+8);
p += symsize;
}
symtab->str = strbuf;
symtab->sym = sym;
free(symbuf);
return 0;
}
Macho* Macho*
machoinit(int fd) machoinit(int fd)
{ {
int i; int i, is64;
uchar hdr[7*4], *cmdp; uchar hdr[7*4], *cmdp;
u32int (*e4)(uchar*); uchar tmp[4];
uint16 (*e2)(uchar*);
uint32 (*e4)(uchar*);
uint64 (*e8)(uchar*);
ulong ncmd, cmdsz, ty, sz, off; ulong ncmd, cmdsz, ty, sz, off;
Macho *m; Macho *m;
if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr) if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr)
return nil; return nil;
if(beload4(hdr) == 0xFEEDFACE) if((beload4(hdr)&~1) == 0xFEEDFACE){
e2 = beload2;
e4 = beload4; e4 = beload4;
else if(leload4(hdr) == 0xFEEDFACE) e8 = beload8;
}else if((leload4(hdr)&~1) == 0xFEEDFACE){
e2 = leload2;
e4 = leload4; e4 = leload4;
else{ e8 = leload8;
}else{
werrstr("bad magic - not mach-o file"); werrstr("bad magic - not mach-o file");
return nil; return nil;
} }
is64 = e4(hdr) == 0xFEEDFACF;
ncmd = e4(hdr+4*4); ncmd = e4(hdr+4*4);
cmdsz = e4(hdr+5*4); cmdsz = e4(hdr+5*4);
if(ncmd > 0x10000 || cmdsz >= 0x01000000){ if(ncmd > 0x10000 || cmdsz >= 0x01000000){
werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz); werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz);
return nil; return nil;
} }
if(is64)
readn(fd, tmp, 4); // skip reserved word in header
m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1); m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1);
if(m == nil) if(m == nil)
return nil; return nil;
m->fd = fd; m->fd = fd;
m->e2 = e2;
m->e4 = e4; m->e4 = e4;
m->e8 = e8;
m->cputype = e4(hdr+1*4); m->cputype = e4(hdr+1*4);
m->subcputype = e4(hdr+2*4); m->subcputype = e4(hdr+2*4);
m->filetype = e4(hdr+3*4); m->filetype = e4(hdr+3*4);
m->ncmd = ncmd; m->ncmd = ncmd;
m->flags = e4(hdr+6*4); m->flags = e4(hdr+6*4);
m->is64 = is64;
m->cmd = (MachoCmd*)(m+1); m->cmd = (MachoCmd*)(m+1);
off = sizeof hdr; off = sizeof hdr;
@ -112,11 +335,10 @@ machoinit(int fd)
ty = e4(cmdp); ty = e4(cmdp);
sz = e4(cmdp+4); sz = e4(cmdp+4);
m->cmd[i].off = off; m->cmd[i].off = off;
unpackseg(cmdp, m, &m->cmd[i], ty, sz); unpackcmd(cmdp, m, &m->cmd[i], ty, sz);
cmdp += sz; cmdp += sz;
off += sz; off += sz;
} }
return m; return m;
} }

View file

@ -1,11 +1,18 @@
typedef struct Macho Macho; typedef struct Macho Macho;
typedef struct MachoCmd MachoCmd; 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 enum
{ {
MachoCpuVax = 1, MachoCpuVax = 1,
MachoCpu68000 = 6, MachoCpu68000 = 6,
MachoCpu386 = 7, MachoCpu386 = 7,
MachoCpuAmd64 = 0x1000007,
MachoCpuMips = 8, MachoCpuMips = 8,
MachoCpu98000 = 10, MachoCpu98000 = 10,
MachoCpuHppa = 11, MachoCpuHppa = 11,
@ -20,6 +27,8 @@ enum
MachoCmdSymtab = 2, MachoCmdSymtab = 2,
MachoCmdSymseg = 3, MachoCmdSymseg = 3,
MachoCmdThread = 4, MachoCmdThread = 4,
MachoCmdDysymtab = 11,
MachoCmdSegment64 = 25,
MachoFileObject = 1, MachoFileObject = 1,
MachoFileExecutable = 2, MachoFileExecutable = 2,
@ -28,40 +37,111 @@ enum
MachoFilePreload = 5 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 offset;
uint32 align;
uint32 reloff;
uint32 nreloc;
uint32 flags;
MachoRel *rel;
};
struct MachoRel
{
uint32 addr;
uint32 symnum;
uint8 pcrel;
uint8 length;
uint8 extrn;
uint8 type;
};
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;
};
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;
};
struct MachoCmd struct MachoCmd
{ {
int type; int type;
ulong off; uint32 off;
ulong size; uint32 size;
struct { MachoSeg seg;
char name[16+1]; MachoSymtab sym;
ulong vmaddr; MachoDysymtab dsym;
ulong vmsize;
ulong fileoff;
ulong filesz;
ulong maxprot;
ulong initprot;
ulong nsect;
ulong flags;
} seg;
struct {
ulong symoff;
ulong nsyms;
ulong stroff;
ulong strsize;
} sym;
}; };
struct Macho struct Macho
{ {
int fd; int fd;
int is64;
uint cputype; uint cputype;
uint subcputype; uint subcputype;
ulong filetype; uint32 filetype;
ulong flags; uint32 flags;
MachoCmd *cmd; MachoCmd *cmd;
uint ncmd; uint ncmd;
u32int (*e4)(uchar*); uint16 (*e2)(uchar*);
uint32 (*e4)(uchar*);
uint64 (*e8)(uchar*);
int (*coreregs)(Macho*, uchar**); int (*coreregs)(Macho*, uchar**);
}; };
@ -69,3 +149,5 @@ Macho *machoopen(char*);
Macho *machoinit(int); Macho *machoinit(int);
void machoclose(Macho*); void machoclose(Macho*);
int coreregsmachopower(Macho*, uchar**); int coreregsmachopower(Macho*, uchar**);
int macholoadrel(Macho*, MachoSect*);
int macholoadsym(Macho*, MachoSymtab*);

View file

@ -71,6 +71,9 @@ elfnm: elfnm.o $LIBDIR/$LIB
demangler: demangler.o $LIBDIR/$LIB demangler: demangler.o $LIBDIR/$LIB
$LD -o $target $prereq -l9 $LD -o $target $prereq -l9
machodump: machodump.o $LIBDIR/$LIB
$LD -o $target $prereq -l9
SunOS.$O: nosys.c SunOS.$O: nosys.c
Darwin.$O: nosys.c Darwin.$O: nosys.c