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