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