| #include <u.h> |
| #include <libc.h> |
| #include <mach.h> |
| #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; 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; |
| 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; 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* |
| 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; i<ncmd; i++){ |
| ty = e4(cmdp); |
| sz = e4(cmdp+4); |
| m->cmd[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); |
| } |