1/* All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
5 * are met:
6 * 1. Redistributions of source code must retain the above copyright
7 *    notice, this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 *    notice, this list of conditions and the following disclaimer in the
10 *    documentation and/or other materials provided with the distribution.
11 * 3. Neither the name of Apple nor the names of any contributors
12 *    may be used to endorse or promote products derived from this software
13 *    without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26*/
27/*
28 * Portions Copyright 2006, Apple Computer, Inc.
29 * Christopher Ryan <ryanc@apple.com>
30*/
31
32#define _FILE_OFFSET_BITS 64
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <arpa/inet.h>
40
41#include "config.h"
42#ifndef HAVE_ASPRINTF
43#include "asprintf.h"
44#endif
45#include "macho.h"
46#include "util.h"
47#include "data.h"
48#include "arcmod.h"
49#include "xar.h"
50
51#define BIT64 0x01000000
52#define PPC   0x00000012
53#define I386  0x00000007
54
55struct machexecutables {
56	struct mach_header mh;
57	struct lc *lc;
58	uint32_t curlc;
59	uint64_t nextlc;
60	char **strings;
61	uint32_t stringsz;
62	uint8_t byteswapped;
63	uint8_t bits;
64};
65
66struct _macho_context{
67	struct fat_header fath;   /* Need to read and buffer the fat header */
68	struct fat_arch *arches;  /* Read and buffer array of arches */
69	uint32_t curarch;          /* Current arch being parsed */
70	struct machexecutables *me;
71	uint64_t nextme;           /* Offset of the next mach header */
72	uint32_t curme;            /* Current me being parsed */
73	unsigned char buffer[512]; /* Place to store incomplete struct info */
74	uint16_t buffersz;         /* Keep track of how much of buffer is used*/
75	uint16_t state;            /* Keep track of what we're looking for */
76	uint64_t curroffset;       /* Current offset in file */
77	uint8_t  byteswapped;      /* Keep track of whether we're byteswapping*/
78};
79
80/* Looking for... */
81enum {
82	lf_fatheader = 0,
83	lf_inc_fatheader,
84	lf_archheader,
85	lf_inc_archheader,
86	lf_machheader,
87	lf_inc_machheader,
88	lf_loadcommand,
89	lf_inc_loadcommand,
90	lf_lcstr,
91	lf_inc_lcstr,
92	lf_none
93};
94
95#define MACHO_CONTEXT(x) ((struct _macho_context *)(*x))
96
97static const char *macho_cpustr(uint32_t cputype)
98{
99	const char *cpustr;
100	switch(cputype) {
101	case PPC: cpustr = "ppc"; break;
102	case I386: cpustr = "i386"; break;
103	case PPC|BIT64: cpustr = "ppc64"; break;
104	default: cpustr = "unknown"; break;
105	};
106	return cpustr;
107}
108
109/* Returns number of bytes consumed during the parsing process */
110static int32_t macho_parse(xar_file_t f, void *in, size_t inlen, struct _macho_context *context)
111{
112	int32_t consumed = 0;
113
114	switch( context->state ) {
115	case(lf_fatheader):
116		if( inlen >= sizeof(struct fat_header) ) {
117			struct fat_header *fh = (struct fat_header *)in;
118			if( fh->magic == 0xcafebabe ) {
119				context->fath.magic = fh->magic;
120				context->fath.nfat_arch = fh->nfat_arch;
121				context->arches = calloc(1,sizeof(struct fat_arch) * fh->nfat_arch);
122				context->me = calloc(1,sizeof(struct machexecutables) * fh->nfat_arch);
123				context->state = lf_archheader;
124				context->byteswapped = 0;
125				context->curarch = 0;
126				consumed = 8;
127				xar_prop_set(f, "contents/type", "Mach-O Fat File");
128			} else if( fh->magic == 0xbebafeca ) {
129				context->fath.magic = xar_swap32(fh->magic);
130				context->fath.nfat_arch = xar_swap32(fh->nfat_arch);
131				context->arches = calloc(1,sizeof(struct fat_arch) * context->fath.nfat_arch);
132				context->me = calloc(1,sizeof(struct machexecutables) * context->fath.nfat_arch);
133				context->state = lf_archheader;
134				context->byteswapped = 1;
135				context->curarch = 0;
136				consumed = 8;
137				xar_prop_set(f, "contents/type", "Mach-O Fat File");
138			} else {
139				context->me = calloc(1,sizeof(struct machexecutables));
140				context->curme = 0;
141				context->state = lf_machheader;
142			}
143		} else {
144			uint32_t *tmp = in;
145			if( (*tmp == 0xcafebabe) || (*tmp == 0xbebafeca) ) {
146				memcpy(context->buffer, in, inlen);
147				context->buffersz = inlen;
148				consumed = (int32_t)inlen;
149				context->state = lf_inc_fatheader;
150			} else {
151				context->me = calloc(1,sizeof(struct machexecutables));
152				context->curme = 0;
153				context->state = lf_machheader;
154			}
155		}
156		break;
157	case lf_archheader:
158		if( inlen >= sizeof(struct fat_arch) ) {
159			struct fat_arch *fa = in;
160			if( context->byteswapped ) {
161				context->arches[context->curarch].cputype = xar_swap32(fa->cputype);
162				context->arches[context->curarch].cpusubtype = xar_swap32(fa->cpusubtype);
163				context->arches[context->curarch].offset = xar_swap32(fa->offset);
164				context->arches[context->curarch].size = xar_swap32(fa->size);
165				context->arches[context->curarch].alighn = xar_swap32(fa->alighn);
166			} else {
167				memcpy(&context->arches[context->curarch], in, sizeof(struct fat_arch));
168			}
169			context->curarch++;
170			if( context->curarch >=context->fath.nfat_arch ) {
171				context->nextme = context->arches[0].offset;
172				context->state = lf_machheader;
173			}
174			consumed = sizeof(struct fat_arch);
175		} else {
176			memcpy(context->buffer, in, inlen);
177			context->buffersz = inlen;
178			consumed = (int32_t)inlen;
179			context->state = lf_inc_archheader;
180		}
181		break;
182	case lf_machheader:
183		if( (context->curroffset+inlen) <= context->nextme )
184			consumed = inlen;
185		else {
186			uint64_t off;
187			unsigned char *tmpin = in;
188			off = context->nextme - context->curroffset;
189			tmpin += off;
190			if( (inlen-off) >= sizeof(struct mach_header) ) {
191				const char *cpustr;
192				char *typestr, *typestr2;
193				struct mach_header *mh = (struct mach_header *)tmpin;
194				switch(mh->magic) {
195				case 0xcffaedfe:
196					context->me[context->curme].bits = 64;
197					context->me[context->curme].byteswapped = 1;
198					break;
199				case 0xcefaedfe:
200					context->me[context->curme].bits = 32;
201					context->me[context->curme].byteswapped = 1;
202					break;
203				case 0xfeedface:
204					context->me[context->curme].bits = 32;
205					break;
206				case 0xfeedfacf:
207					context->me[context->curme].bits = 64;
208					break;
209				default:
210					context->state = lf_none;
211					return inlen;
212				};
213
214				if( context->me[context->curme].byteswapped ) {
215					context->me[context->curme].mh.magic = xar_swap32(mh->magic);
216					context->me[context->curme].mh.cputype = xar_swap32(mh->cputype);
217					context->me[context->curme].mh.cpusubtype = xar_swap32(mh->cpusubtype);
218					context->me[context->curme].mh.filetype = xar_swap32(mh->filetype);
219					context->me[context->curme].mh.ncmds = xar_swap32(mh->ncmds);
220					context->me[context->curme].mh.sizeofcmds = xar_swap32(mh->sizeofcmds);
221					context->me[context->curme].mh.flags = xar_swap32(mh->flags);
222				} else {
223					memcpy(&context->me[context->curme].mh, tmpin, sizeof(struct mach_header));
224				}
225
226				cpustr = macho_cpustr(context->me[context->curme].mh.cputype);
227
228				switch(context->me[context->curme].mh.filetype) {
229				case 0x01: typestr = "Mach-O Object"; break;
230				case 0x02: typestr = "Mach-O Executable"; break;
231				case 0x03: typestr = "Mach-O Fixed VM Library"; break;
232				case 0x04: typestr = "Mach-O core"; break;
233				case 0x05: typestr = "Mach-O Preloaded Executable"; break;
234				case 0x06: typestr = "Mach-O Dylib"; break;
235				case 0x07: typestr = "Mach-O Dylinker"; break;
236				case 0x08: typestr = "Mach-O Bundle"; break;
237				case 0x09: typestr = "Mach-O Stub"; break;
238				default: typestr = "Unknown"; break;
239				};
240
241				if( xar_prop_get(f, "contents/type", (const char **)&typestr2) ) {
242					xar_prop_set(f, "contents/type", typestr);
243				}
244				asprintf(&typestr2, "contents/%s/type", cpustr);
245				xar_prop_set(f, typestr2, typestr);
246				free(typestr2);
247
248				context->me[context->curme].lc = malloc(sizeof(struct lc) * context->me[context->curme].mh.ncmds);
249				context->me[context->curme].strings = malloc(sizeof(char *) * context->me[context->curme].mh.ncmds);
250				context->me[context->curme].curlc = 0;
251				consumed = off + sizeof(struct mach_header);
252				if( context->me[context->curme].bits == 64 )
253					consumed += 4;
254				context->me[context->curme].nextlc = context->curroffset + consumed;
255				context->state = lf_loadcommand;
256			} else {
257				memcpy(context->buffer, tmpin, inlen-off);
258				context->buffersz = inlen-off;
259				consumed = (int32_t)inlen;
260				context->state = lf_inc_machheader;
261			}
262		}
263		break;
264	case lf_loadcommand:
265		if( (context->curroffset+inlen) <= context->me[context->curme].nextlc )
266			consumed = inlen;
267		else {
268			uint64_t off;
269			unsigned char *tmpin = in;
270			off = context->me[context->curme].nextlc - context->curroffset;
271			tmpin += off;
272			if( (inlen-off) >= sizeof(struct lc) ) {
273				if( context->me[context->curme].byteswapped ) {
274					struct lc *lc = (struct lc *)tmpin;
275					context->me[context->curme].lc[context->me[context->curme].curlc].cmd = xar_swap32(lc->cmd);
276					context->me[context->curme].lc[context->me[context->curme].curlc].cmdsize = xar_swap32(lc->cmdsize);
277				} else {
278					memcpy(&context->me[context->curme].lc[context->me[context->curme].curlc], tmpin, sizeof(struct lc));
279				}
280				consumed = off + sizeof(struct lc);
281				context->me[context->curme].nextlc += context->me[context->curme].lc[context->me[context->curme].curlc].cmdsize;
282				if( (context->me[context->curme].lc[context->me[context->curme].curlc].cmd == 0xc) || (context->me[context->curme].lc[context->me[context->curme].curlc].cmd == 0xd) ) {
283					context->state = lf_lcstr;
284				} else {
285					context->me[context->curme].curlc++;
286					if( context->me[context->curme].curlc >= context->me[context->curme].mh.ncmds ) {
287						context->curme++;
288						if( context->fath.nfat_arch ) {
289							if( context->curme >= context->fath.nfat_arch ) {
290								context->state = lf_none;
291							} else {
292								context->nextme = context->arches[context->curme].offset;
293								context->state = lf_machheader;
294							}
295						} else {
296							context->state = lf_none;
297						}
298					}
299				}
300			} else {
301				memcpy(context->buffer, ((char *)in)+off, inlen-off);
302				context->buffersz = inlen-off;
303				consumed = inlen;
304				context->state = lf_inc_loadcommand;
305			}
306		}
307		break;
308	case lf_lcstr:
309		if( inlen >= (context->me[context->curme].lc[context->me[context->curme].curlc].cmdsize-8) ) {
310			const char *tmpstr;
311			uint32_t cmdsize = context->me[context->curme].lc[context->me[context->curme].curlc].cmdsize;
312			uint32_t *offsetp, offset;
313			char *lib, *propstr;
314			offsetp = in;
315			if( context->me[context->curme].byteswapped )
316				offset = xar_swap32(*offsetp);
317			else
318				offset = *offsetp;
319			lib = calloc(1,(cmdsize - offset)+1);
320			memcpy(lib, ((char *)in)+(offset - 8), cmdsize - offset);
321			tmpstr = macho_cpustr(context->me[context->curme].mh.cputype);
322			asprintf(&propstr, "contents/%s/library", tmpstr);
323			xar_prop_create(f, propstr, lib);
324			free(lib);
325			free(propstr);
326
327			consumed = cmdsize-8;
328			context->state = lf_loadcommand;
329			context->me[context->curme].curlc++;
330			if( context->me[context->curme].curlc >= context->me[context->curme].mh.ncmds ) {
331				context->curme++;
332				if( context->fath.nfat_arch ) {
333					if( context->curme >= context->fath.nfat_arch ) {
334						context->state = lf_none;
335					} else {
336						context->nextme = context->arches[context->curme].offset;
337						context->state = lf_machheader;
338					}
339				} else {
340					context->state = lf_none;
341				}
342			}
343		} else {
344			memcpy(context->buffer, in, inlen);
345			context->buffersz = inlen;
346			consumed = inlen;
347			context->state = lf_inc_lcstr;
348		}
349		break;
350	case lf_none:
351	default:
352		consumed = inlen;
353		break;
354	};
355	return consumed;
356}
357
358int32_t xar_macho_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
359	int32_t consumed = 0, total = 0;
360
361	if( strcmp(xar_prop_getkey(p), "data") != 0 )
362		return 0;
363
364	if( !xar_check_prop(x, "contents") )
365		return 0;
366
367	if( !*context ) {
368		*context = calloc(1,sizeof(struct _macho_context));
369	}
370
371	while( total < *inlen ) {
372		consumed = macho_parse(f, ((char *)*in)+total, *inlen-total, MACHO_CONTEXT(context));
373		total += consumed;
374		MACHO_CONTEXT(context)->curroffset += consumed;
375	}
376
377	return 0;
378}
379
380int32_t xar_macho_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
381
382	if( MACHO_CONTEXT(context) ){
383		int i;
384		if( MACHO_CONTEXT(context)->fath.nfat_arch ) {
385			for(i = 0; i < MACHO_CONTEXT(context)->fath.nfat_arch; i++) {
386				if( MACHO_CONTEXT(context)->me[i].lc )
387					free(MACHO_CONTEXT(context)->me[i].lc);
388				if( MACHO_CONTEXT(context)->me[i].strings )
389					free(MACHO_CONTEXT(context)->me[i].strings);
390			}
391		} else {
392			if( MACHO_CONTEXT(context)->me ) {
393				if( MACHO_CONTEXT(context)->me[0].lc )
394					free(MACHO_CONTEXT(context)->me[0].lc);
395				if( MACHO_CONTEXT(context)->me[0].strings )
396					free(MACHO_CONTEXT(context)->me[0].strings);
397			}
398		}
399		if( MACHO_CONTEXT(context)->me )
400			free(MACHO_CONTEXT(context)->me);
401		if( MACHO_CONTEXT(context)->arches )
402			free(MACHO_CONTEXT(context)->arches);
403		free(*context);
404	}
405
406	return 0;
407}
408