diff options
author | 2008-09-08 07:02:56 +0000 | |
---|---|---|
committer | 2008-09-08 07:02:56 +0000 | |
commit | b14fee9febef245dc43042a0feaa8620dbcc02ee (patch) | |
tree | ea12455d0f2c7d6cc029172cb3425656f52647e1 /paxmacho.c | |
parent | - add OSABI/EABI support to scanelf --osabi/--eabi to scanelf. Update elf.h (diff) | |
download | pax-utils-b14fee9febef245dc43042a0feaa8620dbcc02ee.tar.gz pax-utils-b14fee9febef245dc43042a0feaa8620dbcc02ee.tar.bz2 pax-utils-b14fee9febef245dc43042a0feaa8620dbcc02ee.zip |
Initial commit of Mach-O related files. (bug #236512)
- added macho.h (Mach-O definitions) and scanmacho.c (the scanelf
equivalent for Mach-O files)
- changed the Makefile to compile scanmacho (tested compilation on
Darwin and Solaris)
- changed paxinc.h to include macho.h and paxmacho.h
- extended paxmacho.[ch] with relevant code to read meta information
from Mach-O files
Diffstat (limited to 'paxmacho.c')
-rw-r--r-- | paxmacho.c | 415 |
1 files changed, 355 insertions, 60 deletions
@@ -1,29 +1,35 @@ /* - * Copyright 2003-2007 Gentoo Foundation + * Copyright 2003-2008 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 - * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxmacho.c,v 1.6 2008/01/17 04:37:19 solar Exp $ + * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxmacho.c,v 1.7 2008/09/08 07:02:56 grobian Exp $ * * Copyright 2005-2007 Ned Ludd - <solar@gentoo.org> * Copyright 2005-2007 Mike Frysinger - <vapier@gentoo.org> + * 2008 Fabian Groffen - <grobian@gentoo.org> */ #include "paxinc.h" -const char * const argv0 = "paxmacho"; +/* lil' static string pool */ +static const char *STR_BE = "BE"; +static const char *STR_LE = "LE"; +static const char *STR_PPC = "ppc"; +static const char *STR_PPC64 = "ppc64"; +static const char *STR_I386 = "i386"; +static const char *STR_X86_64 = "x86_64"; +static const char *STR_ARM = "arm"; /* iPhone */ +static const char *STR_UNKNOWN = "unknown"; -/* - * Setup a bunch of helper functions to translate - * binary defines into readable strings. - */ #define QUERY(n) { #n, n } typedef struct { - const char *str; - int value; + const char *str; + int value; } pairtype; + static inline const char *find_pairtype(pairtype *pt, int type) { int i; - for (i = 0; pt[i].str; ++i) + for (i = 0; pt[i].str; i++) if (type == pt[i].value) return pt[i].str; return "UNKNOWN TYPE"; @@ -38,24 +44,173 @@ static pairtype macho_mh_type[] = { QUERY(MH_PRELOAD), QUERY(MH_CORE), QUERY(MH_DYLINKER), + QUERY(MH_DYLIB_STUB), + QUERY(MH_DSYM), + { 0, 0 } +}; +const char *get_machomhtype(fatobj *fobj) +{ + /* can use 32-bits header, since 64 and 32 are aligned here */ + if (fobj->swapped) { + return find_pairtype(macho_mh_type, + bswap_32(fobj->mhdr.hdr32->filetype)); + } else { + return find_pairtype(macho_mh_type, fobj->mhdr.hdr32->filetype); + } +} + +/* translate misc mach-o MH_ flags */ +static pairtype macho_mh_flag[] = { + QUERY(MH_NOUNDEFS), + QUERY(MH_INCRLINK), + QUERY(MH_DYLDLINK), + QUERY(MH_TWOLEVEL), + QUERY(MH_BINDATLOAD), + QUERY(MH_PREBOUND), + QUERY(MH_PREBINDABLE), + QUERY(MH_NOFIXPREBINDING), + QUERY(MH_ALLMODSBOUND), + QUERY(MH_CANONICAL), + QUERY(MH_SPLIT_SEGS), + QUERY(MH_FORCE_FLAT), + QUERY(MH_SUBSECTIONS_VIA_SYMBOLS), + QUERY(MH_NOMULTIDEFS), + { 0, 0 } +}; +void get_machomhflags(fatobj *fobj, char **ret, size_t *ret_len) +{ + uint32_t flags; + int i; + char first = 1; + + /* can use 32-bits header, since 64 and 32 are aligned here */ + if (fobj->swapped) { + flags = bswap_32(fobj->mhdr.hdr32->flags); + } else { + flags = fobj->mhdr.hdr32->flags; + } + + for (i = 0; macho_mh_flag[i].str; i++) { + if ((flags & macho_mh_flag[i].value) == macho_mh_flag[i].value) { + if (!first) + xchrcat(ret, ',', ret_len); + xstrcat(ret, macho_mh_flag[i].str, ret_len); + first = 0; + } + } +} + +static pairtype macho_cputype[] = { + QUERY(CPU_TYPE_POWERPC), + QUERY(CPU_TYPE_I386), + QUERY(CPU_TYPE_ARM), + QUERY(CPU_TYPE_POWERPC64), + QUERY(CPU_TYPE_X86_64), { 0, 0 } }; -const char *get_machomhtype(int mh_type) +const char *get_machocputype(fatobj *fobj) { - return find_pairtype(macho_mh_type, mh_type); + const char *ret; + /* can use 32-bits header, since 64 and 32 are aligned here */ + if (fobj->swapped) { + ret = find_pairtype(macho_cputype, bswap_32(fobj->mhdr.hdr32->cputype)); + } else { + ret = find_pairtype(macho_cputype, fobj->mhdr.hdr32->cputype); + } + return(ret + sizeof("CPU_TYPE_") - 1); +} +/* translate cpusubtypes */ +static pairtype macho_cpusubtypeppc[] = { + QUERY(CPU_SUBTYPE_POWERPC_ALL), + QUERY(CPU_SUBTYPE_POWERPC_601), + QUERY(CPU_SUBTYPE_POWERPC_602), + QUERY(CPU_SUBTYPE_POWERPC_603), + QUERY(CPU_SUBTYPE_POWERPC_603e), + QUERY(CPU_SUBTYPE_POWERPC_603ev), + QUERY(CPU_SUBTYPE_POWERPC_604), + QUERY(CPU_SUBTYPE_POWERPC_604e), + QUERY(CPU_SUBTYPE_POWERPC_620), + QUERY(CPU_SUBTYPE_POWERPC_750), + QUERY(CPU_SUBTYPE_POWERPC_7400), + QUERY(CPU_SUBTYPE_POWERPC_7450), + QUERY(CPU_SUBTYPE_POWERPC_970), + { 0, 0 } +}; +static pairtype macho_cpusubtypex86[] = { + QUERY(CPU_SUBTYPE_I386_ALL), + QUERY(CPU_SUBTYPE_486), + QUERY(CPU_SUBTYPE_586), + QUERY(CPU_SUBTYPE_PENTIUM_3), + QUERY(CPU_SUBTYPE_PENTIUM_M), + QUERY(CPU_SUBTYPE_PENTIUM_4), + QUERY(CPU_SUBTYPE_ITANIUM), + QUERY(CPU_SUBTYPE_XEON), + { 0, 0 } +}; +const char *get_machosubcputype(fatobj *fobj) +{ + const char *ret; + /* can use 32-bits header, since 64 and 32 are aligned here */ + uint32_t type = fobj->mhdr.hdr32->cputype; + if (fobj->swapped) + type = bswap_32(type); + + if (type == CPU_TYPE_I386 || type == CPU_TYPE_X86_64) { + type = fobj->mhdr.hdr32->cpusubtype; + if (fobj->swapped) + type = bswap_32(type); + ret = find_pairtype(macho_cpusubtypex86, type); + return(ret + sizeof("CPU_SUBTYPE_") - 1); + } else if (type == CPU_TYPE_POWERPC || type == CPU_TYPE_POWERPC64) { + type = fobj->mhdr.hdr32->cpusubtype; + if (fobj->swapped) + type = bswap_32(type); + ret = find_pairtype(macho_cpusubtypeppc, type); + return(ret + sizeof("CPU_SUBTYPE_") - 1); + } else { + return(STR_UNKNOWN); + } } -/* Read a macho into memory */ -#define IS_MACHO_MAGIC(m) \ - (m == MH_MAGIC || m == MH_CIGAM || \ - m == MH_MAGIC_64 || m == MH_CIGAM_64) -#define DO_WE_LIKE_MACHO(buff) 1 -machoobj *readmacho(const char *filename) + +/* Determines the type of this object, and sets the right 32-bit or + * 64-bits pointer. The ismach64 flag is filled in appropriately. The + * return of this function is the read magic value, or 0 when the file + * is not recognised. */ +inline static uint32_t read_mach_header(fatobj *fobj, void *addr) +{ + struct mach_header *mhdr = (struct mach_header*)addr; + fobj->mhdata = (char *)addr; + switch (mhdr->magic) { + case MH_CIGAM: + fobj->swapped = 1; + case MH_MAGIC: + /* 32-bits */ + fobj->ismach64 = 0; + fobj->mhdr.hdr32 = mhdr; + fobj->isbigendian = + (*fobj->mhdata == (char)(MH_MAGIC >> 24) ? 1 : 0); + return(mhdr->magic); + case MH_CIGAM_64: + fobj->swapped = 1; + case MH_MAGIC_64: + /* 64-bits */ + fobj->ismach64 = 1; + fobj->mhdr.hdr64 = (struct mach_header_64*)addr; + fobj->isbigendian = + (*fobj->mhdata == (char)(MH_MAGIC_64 >> 24) ? 1 : 0); + return(mhdr->magic); + default: + return(0); /* unrecognised file */ + } +} + +/* Read a macho into memory, returning a fatobj struct with at least one + * arch. */ +fatobj *readmacho(const char *filename) { struct stat st; int fd; - machoobj *macho; - struct mach_header *mhdr; if (stat(filename, &st) == -1) return NULL; @@ -63,59 +218,199 @@ machoobj *readmacho(const char *filename) if ((fd = open(filename, O_RDONLY)) == -1) return NULL; - /* make sure we have enough bytes to scan e_ident */ - if (st.st_size <= sizeof(struct mach_header)) - goto close_fd_and_return; + return(readmacho_fd(filename, fd, 0)); +} - macho = (machoobj*)malloc(sizeof(*macho)); - if (macho == NULL) - goto close_fd_and_return; - memset(macho, 0x00, sizeof(*macho)); - - macho->fd = fd; - macho->len = st.st_size; - macho->data = (char*)mmap(0, macho->len, PROT_READ, MAP_PRIVATE, fd, 0); - if (macho->data == (char*)MAP_FAILED) { - warn("mmap on '%s' of %li bytes failed :(", filename, (unsigned long)macho->len); - goto free_macho_and_return; +fatobj *readmacho_fd(const char *filename, int fd, size_t len) +{ + char *data; + fatobj *ret; + + if (len == 0) { + struct stat st; + if (fstat(fd, &st) == -1) + return NULL; + len = st.st_size; + if (len == 0) + return NULL; } - mhdr = (struct mach_header*)macho->data; - do_reverse_endian = (mhdr->magic == MH_CIGAM || mhdr->magic == MH_CIGAM_64); - macho->macho_class = (EGET(mhdr->magic) == MH_MAGIC ? MH_MAGIC : MH_MAGIC_64); - - if (!IS_MACHO_MAGIC(mhdr->magic)) /* make sure we have an macho */ - goto unmap_data_and_return; - if (1 || !DO_WE_LIKE_MACHO(mhdr)) { /* check class and stuff */ - warn("we no likey %s: {%i:%s}", - filename, - (int)EGET(mhdr->filetype), get_machomhtype(EGET(mhdr->filetype))); - goto unmap_data_and_return; + /* make sure we have enough bytes to scan */ + if (len <= sizeof(struct fat_header)) + goto close_fd_and_return; + + data = (char*)mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (data == (char*)MAP_FAILED) { + warn("mmap on '%s' of %llu bytes failed :(", + filename, (unsigned long long)len); + goto close_fd_and_return; } - macho->filename = filename; - macho->base_filename = strrchr(filename, '/'); - if (macho->base_filename == NULL) - macho->base_filename = macho->filename; - else - macho->base_filename = macho->base_filename + 1; - macho->mhdr = (void*)macho->data; + ret = readmacho_buffer(filename, data, len); - return macho; + if (ret != NULL) { + ret->fd = fd; + return(ret); + } -unmap_data_and_return: - munmap(macho->data, macho->len); -free_macho_and_return: - free(macho); + munmap(data, len); close_fd_and_return: close(fd); return NULL; } +fatobj *readmacho_buffer(const char *filename, char *buffer, size_t buffer_len) +{ + struct fat_header *fhdr; + fatobj *ret = malloc(sizeof(fatobj)); + + ret->fd = -1; + ret->filename = filename; + ret->base_filename = strrchr(ret->filename, '/'); + ret->base_filename = + (ret->base_filename == NULL ? ret->filename : ret->base_filename + 1); + ret->len = buffer_len; + ret->data = buffer; + ret->swapped = 0; + + fhdr = (struct fat_header*)ret->data; + /* Check what kind of file this is. Unfortunately we don't have + * configure, so we don't know if we're on big or little endian, so + * we cannot check if the fat_header is in bigendian like it should. + */ + if (fhdr->magic == FAT_MAGIC || fhdr->magic == FAT_CIGAM) { + /* we're indeed in a FAT file */ + int i; + fatobj *fobj = ret; + struct fat_arch *farch; + char *dptr = ret->data + sizeof(struct fat_header); + char swapped = 0; + uint32_t narchs = fhdr->nfat_arch; + uint32_t offset; + + /* FAT headers are always big-endian, so swap if on little + * machines... */ + if (fhdr->magic == FAT_CIGAM) { + swapped = 1; + narchs = bswap_32(narchs); + } + + for (i = 1; i <= narchs; i++) { + farch = (struct fat_arch*)dptr; + offset = swapped ? bswap_32(farch->offset) : farch->offset; + if (read_mach_header(fobj, ret->data + offset) == 0) + return(NULL); + if (i < narchs) { + fobj = fobj->next = malloc(sizeof(fatobj)); + memset(fobj, 0, sizeof(fatobj)); + /* filename and size are necessary for printing */ + fobj->filename = ret->filename; + fobj->base_filename = ret->base_filename; + fobj->len = ret->len; + } + dptr += sizeof(struct fat_arch); + } + } else { + /* simple Mach-O file, treat as single arch FAT file */ + if (read_mach_header(ret, ret->data) == 0) + return(NULL); + ret->next = NULL; + } + + return(ret); +} + + /* undo the readmacho() stuff */ -void unreadmacho(machoobj *macho) +void unreadmacho(fatobj *macho) { - munmap(macho->data, macho->len); - close(macho->fd); + if (macho->data != NULL) { + munmap(macho->data, macho->len); + close(macho->fd); + } + /* free all arches recursively */ + if (macho->next != NULL) + unreadmacho(macho->next); free(macho); } + +/* Returns the first load_command in the file (after the mach_header) + * and allocates a loadcmd struct to store it together with some + * convenience data. The struct can be manually freed, if not traversed + * until the end of the load section. */ +loadcmd *firstloadcmd(fatobj *fobj) +{ + loadcmd *ret = malloc(sizeof(loadcmd)); + ret->data = fobj->mhdata + + (fobj->ismach64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header)); + ret->lcmd = (struct load_command*)ret->data; + ret->cleft = fobj->mhdr.hdr32->ncmds; /* 32 and 64 bits are aligned here */ + ret->align = (fobj->ismach64 ? 8 : 4); + ret->swapped = fobj->swapped; + if (ret->swapped) + ret->cleft = bswap_32(ret->cleft); + /* a bit useless, but a nice consistency check for ourselves now */ + if (ret->lcmd->cmdsize % ret->align != 0) { + warn("cmdsize isn't properly aligned on %d bytes boundary (%d)", + ret->align, ret->lcmd->cmdsize); + } + + return(ret); +} + +/* Sets up the given loadcmd struct with the next load command, or frees + * it if there are no more load commands. If a new load command was + * loaded, 1 is returned, 0 otherwise. This behaviour is useful when + * looping over all load commands, since firstloadcmd will allocate the + * loadcmd struct, and nextloadcmd will free it once all load commands + * have been seen. */ +int nextloadcmd(loadcmd* lcmd) +{ + uint32_t size = lcmd->lcmd->cmdsize; + if (lcmd->swapped) + size = bswap_32(size); + + if (--(lcmd->cleft) == 0) { + free(lcmd); + return(0); + } + + if (size % lcmd->align != 0) { + /* fix alignment, this should actually never happen, but the doc + * says we have to pad if the alignment sucks */ + size += lcmd->align - (size % lcmd->align); + } + lcmd->data += size; + lcmd->lcmd = (struct load_command*)lcmd->data; + + return(1); +} + +const char *get_machoendian(fatobj *fobj) +{ + if (fobj->isbigendian) + return(STR_BE); + else + return(STR_LE); +} + +const char *get_machomtype(fatobj *fobj) +{ + uint32_t cputype = fobj->mhdr.hdr32->cputype; + if (fobj->swapped) + cputype = bswap_32(cputype); + switch (cputype) { + case CPU_TYPE_POWERPC: + return(STR_PPC); + case CPU_TYPE_I386: + return(STR_I386); + case CPU_TYPE_ARM: + return(STR_ARM); + case CPU_TYPE_POWERPC64: + return(STR_PPC64); + case CPU_TYPE_X86_64: + return(STR_X86_64); + default: + return(STR_UNKNOWN); + } +} |