#include #include #include #include "macho.h" /* http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/ */ Macho* machoopen(char *name) { int fd; Macho *m; if((fd = open(name, OREAD)) < 0) return nil; m = machoinit(fd); if(m == nil) close(fd); return m; } static int unpackcmd(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz) { uint32 (*e4)(uchar*); uint64 (*e8)(uchar*); MachoSect *s; int i; e4 = m->e4; e8 = m->e8; 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 = 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; iseg.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; iseg.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; 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; } 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; inreloc; 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; insym; 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* machoinit(int fd) { int i, is64; uchar hdr[7*4], *cmdp; uchar tmp[4]; uint16 (*e2)(uchar*); uint32 (*e4)(uchar*); uint64 (*e8)(uchar*); ulong ncmd, cmdsz, ty, sz, off; Macho *m; if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr) return nil; if((beload4(hdr)&~1) == 0xFEEDFACE){ e2 = beload2; e4 = beload4; e8 = beload8; }else if((leload4(hdr)&~1) == 0xFEEDFACE){ e2 = leload2; e4 = leload4; e8 = leload8; }else{ werrstr("bad magic - not mach-o file"); return nil; } is64 = e4(hdr) == 0xFEEDFACF; ncmd = e4(hdr+4*4); cmdsz = e4(hdr+5*4); if(ncmd > 0x10000 || cmdsz >= 0x01000000){ werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz); return nil; } if(is64) readn(fd, tmp, 4); // skip reserved word in header m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1); if(m == nil) return nil; m->fd = fd; m->e2 = e2; m->e4 = e4; m->e8 = e8; m->cputype = e4(hdr+1*4); m->subcputype = e4(hdr+2*4); m->filetype = e4(hdr+3*4); m->ncmd = ncmd; m->flags = e4(hdr+6*4); m->is64 = is64; m->cmd = (MachoCmd*)(m+1); off = sizeof hdr; cmdp = (uchar*)(m->cmd+ncmd); if(readn(fd, cmdp, cmdsz) != cmdsz){ werrstr("reading cmds: %r"); free(m); return nil; } for(i=0; icmd[i].off = off; unpackcmd(cmdp, m, &m->cmd[i], ty, sz); cmdp += sz; off += sz; } return m; } void machoclose(Macho *m) { close(m->fd); free(m); }