aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Groffen <grobian@gentoo.org>2008-09-08 07:02:56 +0000
committerFabian Groffen <grobian@gentoo.org>2008-09-08 07:02:56 +0000
commitb14fee9febef245dc43042a0feaa8620dbcc02ee (patch)
treeea12455d0f2c7d6cc029172cb3425656f52647e1 /paxmacho.c
parent- add OSABI/EABI support to scanelf --osabi/--eabi to scanelf. Update elf.h (diff)
downloadpax-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.c415
1 files changed, 355 insertions, 60 deletions
diff --git a/paxmacho.c b/paxmacho.c
index e7eddab..31d03c8 100644
--- a/paxmacho.c
+++ b/paxmacho.c
@@ -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);
+ }
+}