plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

macho.c (6690B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <mach.h>
      4 #include "macho.h"
      5 
      6 /*
      7 http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/
      8 */
      9 
     10 Macho*
     11 machoopen(char *name)
     12 {
     13 	int fd;
     14 	Macho *m;
     15 
     16 	if((fd = open(name, OREAD)) < 0)
     17 		return nil;
     18 	m = machoinit(fd);
     19 	if(m == nil)
     20 		close(fd);
     21 	return m;
     22 }
     23 
     24 static int
     25 unpackcmd(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz)
     26 {
     27 	uint32 (*e4)(uchar*);
     28 	uint64 (*e8)(uchar*);
     29 	MachoSect *s;
     30 	int i;
     31 
     32 	e4 = m->e4;
     33 	e8 = m->e8;
     34 
     35 	c->type = type;
     36 	c->size = sz;
     37 	switch(type){
     38 	default:
     39 		return -1;
     40 	case MachoCmdSegment:
     41 		if(sz < 56)
     42 			return -1;
     43 		strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8);
     44 		c->seg.vmaddr = e4(p+24);
     45 		c->seg.vmsize = e4(p+28);
     46 		c->seg.fileoff = e4(p+32);
     47 		c->seg.filesz = e4(p+36);
     48 		c->seg.maxprot = e4(p+40);
     49 		c->seg.initprot = e4(p+44);
     50 		c->seg.nsect = e4(p+48);
     51 		c->seg.flags = e4(p+52);
     52 		c->seg.sect = mallocz(c->seg.nsect * sizeof c->seg.sect[0], 1);
     53 		if(c->seg.sect == nil)
     54 			return -1;
     55 		if(sz < 56+c->seg.nsect*68)
     56 			return -1;
     57 		p += 56;
     58 		for(i=0; i<c->seg.nsect; i++) {
     59 			s = &c->seg.sect[i];
     60 			strecpy(s->name, s->name+sizeof s->name, (char*)p+0);
     61 			strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16);
     62 			s->addr = e4(p+32);
     63 			s->size = e4(p+36);
     64 			s->offset = e4(p+40);
     65 			s->align = e4(p+44);
     66 			s->reloff = e4(p+48);
     67 			s->nreloc = e4(p+52);
     68 			s->flags = e4(p+56);
     69 			// p+60 and p+64 are reserved
     70 			p += 68;
     71 		}
     72 		break;
     73 	case MachoCmdSegment64:
     74 		if(sz < 72)
     75 			return -1;
     76 		strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8);
     77 		c->seg.vmaddr = e8(p+24);
     78 		c->seg.vmsize = e8(p+32);
     79 		c->seg.fileoff = e8(p+40);
     80 		c->seg.filesz = e8(p+48);
     81 		c->seg.maxprot = e4(p+56);
     82 		c->seg.initprot = e4(p+60);
     83 		c->seg.nsect = e4(p+64);
     84 		c->seg.flags = e4(p+68);
     85 		c->seg.sect = mallocz(c->seg.nsect * sizeof c->seg.sect[0], 1);
     86 		if(c->seg.sect == nil)
     87 			return -1;
     88 		if(sz < 72+c->seg.nsect*80)
     89 			return -1;
     90 		p += 72;
     91 		for(i=0; i<c->seg.nsect; i++) {
     92 			s = &c->seg.sect[i];
     93 			strecpy(s->name, s->name+sizeof s->name, (char*)p+0);
     94 			strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16);
     95 			s->addr = e8(p+32);
     96 			s->size = e8(p+40);
     97 			s->offset = e4(p+48);
     98 			s->align = e4(p+52);
     99 			s->reloff = e4(p+56);
    100 			s->nreloc = e4(p+60);
    101 			s->flags = e4(p+64);
    102 			// p+68, p+72, and p+76 are reserved
    103 			p += 80;
    104 		}
    105 		break;
    106 	case MachoCmdSymtab:
    107 		if(sz < 24)
    108 			return -1;
    109 		c->sym.symoff = e4(p+8);
    110 		c->sym.nsym = e4(p+12);
    111 		c->sym.stroff = e4(p+16);
    112 		c->sym.strsize = e4(p+20);
    113 		break;
    114 	case MachoCmdDysymtab:
    115 		if(sz < 80)
    116 			return -1;
    117 		c->dsym.ilocalsym = e4(p+8);
    118 		c->dsym.nlocalsym = e4(p+12);
    119 		c->dsym.iextdefsym = e4(p+16);
    120 		c->dsym.nextdefsym = e4(p+20);
    121 		c->dsym.iundefsym = e4(p+24);
    122 		c->dsym.nundefsym = e4(p+28);
    123 		c->dsym.tocoff = e4(p+32);
    124 		c->dsym.ntoc = e4(p+36);
    125 		c->dsym.modtaboff = e4(p+40);
    126 		c->dsym.nmodtab = e4(p+44);
    127 		c->dsym.extrefsymoff = e4(p+48);
    128 		c->dsym.nextrefsyms = e4(p+52);
    129 		c->dsym.indirectsymoff = e4(p+56);
    130 		c->dsym.nindirectsyms = e4(p+60);
    131 		c->dsym.extreloff = e4(p+64);
    132 		c->dsym.nextrel = e4(p+68);
    133 		c->dsym.locreloff = e4(p+72);
    134 		c->dsym.nlocrel = e4(p+76);
    135 		break;
    136 	}
    137 	return 0;
    138 }
    139 
    140 int
    141 macholoadrel(Macho *m, MachoSect *sect)
    142 {
    143 	MachoRel *rel, *r;
    144 	uchar *buf, *p;
    145 	int i, n;
    146 	uint32 v;
    147 
    148 	if(sect->rel != nil || sect->nreloc == 0)
    149 		return 0;
    150 	rel = mallocz(sect->nreloc * sizeof r[0], 1);
    151 	if(rel == nil)
    152 		return -1;
    153 	n = sect->nreloc * 8;
    154 	buf = mallocz(n, 1);
    155 	if(buf == nil) {
    156 		free(rel);
    157 		return -1;
    158 	}
    159 	if(seek(m->fd, sect->reloff, 0) < 0 || readn(m->fd, buf, n) != n) {
    160 		free(rel);
    161 		free(buf);
    162 		return -1;
    163 	}
    164 	for(i=0; i<sect->nreloc; i++) {
    165 		r = &rel[i];
    166 		p = buf+i*8;
    167 		r->addr = m->e4(p);
    168 
    169 		// TODO(rsc): Wrong interpretation for big-endian bitfields?
    170 		v = m->e4(p+4);
    171 		r->symnum = v & 0xFFFFFF;
    172 		v >>= 24;
    173 		r->pcrel = v&1;
    174 		v >>= 1;
    175 		r->length = 1<<(v&3);
    176 		v >>= 2;
    177 		r->extrn = v&1;
    178 		v >>= 1;
    179 		r->type = v;
    180 	}
    181 	sect->rel = rel;
    182 	free(buf);
    183 	return 0;
    184 }
    185 
    186 int
    187 macholoadsym(Macho *m, MachoSymtab *symtab)
    188 {
    189 	char *strbuf;
    190 	uchar *symbuf, *p;
    191 	int i, n, symsize;
    192 	MachoSym *sym, *s;
    193 	uint32 v;
    194 
    195 	if(symtab->sym != nil)
    196 		return 0;
    197 
    198 	strbuf = mallocz(symtab->strsize, 1);
    199 	if(strbuf == nil)
    200 		return -1;
    201 	if(seek(m->fd, symtab->stroff, 0) < 0 || readn(m->fd, strbuf, symtab->strsize) != symtab->strsize) {
    202 		free(strbuf);
    203 		return -1;
    204 	}
    205 
    206 	symsize = 12;
    207 	if(m->is64)
    208 		symsize = 16;
    209 	n = symtab->nsym * symsize;
    210 	symbuf = mallocz(n, 1);
    211 	if(symbuf == nil) {
    212 		free(strbuf);
    213 		return -1;
    214 	}
    215 	if(seek(m->fd, symtab->symoff, 0) < 0 || readn(m->fd, symbuf, n) != n) {
    216 		free(strbuf);
    217 		free(symbuf);
    218 		return -1;
    219 	}
    220 	sym = mallocz(symtab->nsym * sizeof sym[0], 1);
    221 	if(sym == nil) {
    222 		free(strbuf);
    223 		free(symbuf);
    224 		return -1;
    225 	}
    226 	p = symbuf;
    227 	for(i=0; i<symtab->nsym; i++) {
    228 		s = &sym[i];
    229 		v = m->e4(p);
    230 		if(v >= symtab->strsize) {
    231 			free(strbuf);
    232 			free(symbuf);
    233 			free(sym);
    234 			return -1;
    235 		}
    236 		s->name = strbuf + v;
    237 		s->type = p[4];
    238 		s->sectnum = p[5];
    239 		s->desc = m->e2(p+6);
    240 		if(m->is64)
    241 			s->value = m->e8(p+8);
    242 		else
    243 			s->value = m->e4(p+8);
    244 		p += symsize;
    245 	}
    246 	symtab->str = strbuf;
    247 	symtab->sym = sym;
    248 	free(symbuf);
    249 	return 0;
    250 }
    251 
    252 Macho*
    253 machoinit(int fd)
    254 {
    255 	int i, is64;
    256 	uchar hdr[7*4], *cmdp;
    257 	uchar tmp[4];
    258 	uint16 (*e2)(uchar*);
    259 	uint32 (*e4)(uchar*);
    260 	uint64 (*e8)(uchar*);
    261 	ulong ncmd, cmdsz, ty, sz, off;
    262 	Macho *m;
    263 
    264 	if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr)
    265 		return nil;
    266 
    267 	if((beload4(hdr)&~1) == 0xFEEDFACE){
    268 		e2 = beload2;
    269 		e4 = beload4;
    270 		e8 = beload8;
    271 	}else if((leload4(hdr)&~1) == 0xFEEDFACE){
    272 		e2 = leload2;
    273 		e4 = leload4;
    274 		e8 = leload8;
    275 	}else{
    276 		werrstr("bad magic - not mach-o file");
    277 		return nil;
    278 	}
    279 	is64 = e4(hdr) == 0xFEEDFACF;
    280 	ncmd = e4(hdr+4*4);
    281 	cmdsz = e4(hdr+5*4);
    282 	if(ncmd > 0x10000 || cmdsz >= 0x01000000){
    283 		werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz);
    284 		return nil;
    285 	}
    286 	if(is64)
    287 		readn(fd, tmp, 4);	// skip reserved word in header
    288 
    289 	m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1);
    290 	if(m == nil)
    291 		return nil;
    292 
    293 	m->fd = fd;
    294 	m->e2 = e2;
    295 	m->e4 = e4;
    296 	m->e8 = e8;
    297 	m->cputype = e4(hdr+1*4);
    298 	m->subcputype = e4(hdr+2*4);
    299 	m->filetype = e4(hdr+3*4);
    300 	m->ncmd = ncmd;
    301 	m->flags = e4(hdr+6*4);
    302 	m->is64 = is64;
    303 
    304 	m->cmd = (MachoCmd*)(m+1);
    305 	off = sizeof hdr;
    306 	cmdp = (uchar*)(m->cmd+ncmd);
    307 	if(readn(fd, cmdp, cmdsz) != cmdsz){
    308 		werrstr("reading cmds: %r");
    309 		free(m);
    310 		return nil;
    311 	}
    312 
    313 	for(i=0; i<ncmd; i++){
    314 		ty = e4(cmdp);
    315 		sz = e4(cmdp+4);
    316 		m->cmd[i].off = off;
    317 		unpackcmd(cmdp, m, &m->cmd[i], ty, sz);
    318 		cmdp += sz;
    319 		off += sz;
    320 	}
    321 	return m;
    322 }
    323 
    324 void
    325 machoclose(Macho *m)
    326 {
    327 	close(m->fd);
    328 	free(m);
    329 }