Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <mach.h>
4 #include "macho.h"
6 /*
7 http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/
8 */
10 Macho*
11 machoopen(char *name)
12 {
13 int fd;
14 Macho *m;
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 }
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;
32 e4 = m->e4;
33 e8 = m->e8;
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;
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;
137 return 0;
140 int
141 macholoadrel(Macho *m, MachoSect *sect)
143 MachoRel *rel, *r;
144 uchar *buf, *p;
145 int i, n;
146 uint32 v;
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;
159 if(seek(m->fd, sect->reloff, 0) < 0 || readn(m->fd, buf, n) != n) {
160 free(rel);
161 free(buf);
162 return -1;
164 for(i=0; i<sect->nreloc; i++) {
165 r = &rel[i];
166 p = buf+i*8;
167 r->addr = m->e4(p);
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;
181 sect->rel = rel;
182 free(buf);
183 return 0;
186 int
187 macholoadsym(Macho *m, MachoSymtab *symtab)
189 char *strbuf;
190 uchar *symbuf, *p;
191 int i, n, symsize;
192 MachoSym *sym, *s;
193 uint32 v;
195 if(symtab->sym != nil)
196 return 0;
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;
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;
215 if(seek(m->fd, symtab->symoff, 0) < 0 || readn(m->fd, symbuf, n) != n) {
216 free(strbuf);
217 free(symbuf);
218 return -1;
220 sym = mallocz(symtab->nsym * sizeof sym[0], 1);
221 if(sym == nil) {
222 free(strbuf);
223 free(symbuf);
224 return -1;
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;
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;
246 symtab->str = strbuf;
247 symtab->sym = sym;
248 free(symbuf);
249 return 0;
252 Macho*
253 machoinit(int fd)
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;
264 if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr)
265 return nil;
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;
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;
286 if(is64)
287 readn(fd, tmp, 4); // skip reserved word in header
289 m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1);
290 if(m == nil)
291 return nil;
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;
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;
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;
321 return m;
324 void
325 machoclose(Macho *m)
327 close(m->fd);
328 free(m);