Commit message: Fix #9970 - heap oobread in mach0 parser (#10026)
CVE: CVE-2018-11380
CWE: CWE-125
Vulnerability description: The parse_import_ptr() function in radare2 2.5.0 allows remote attackers to cause a denial of service (heap-based out-of-bounds read and application crash) via a crafted Mach-O file.

Project: radare2
File: mach0.c

struct MACH0_(obj_t)* MACH0_(mach0_new)(const char* file, bool verbose) {
	ut8 *buf;
	struct MACH0_(obj_t) *bin;
	if (!(bin = malloc (sizeof (struct MACH0_(obj_t))))) {
		return NULL;
	}
	memset (bin, 0, sizeof (struct MACH0_(obj_t)));
	bin->verbose = verbose;
	bin->file = file;
	if (!(buf = (ut8*)r_file_slurp (file, &bin->size))) {
		return MACH0_(mach0_free)(bin);
	}
	bin->b = r_buf_new ();
	if (!r_buf_set_bytes (bin->b, buf, bin->size)) {
		free (buf);
		return MACH0_(mach0_free)(bin);
	}
	free (buf);
	bin->dyld_info = NULL;
	if (!init (bin)) {
		return MACH0_(mach0_free)(bin);
	}
	bin->imports_by_ord_size = 0;
	bin->imports_by_ord = NULL;
	return bin;
}

Project: radare2
File: mach0.c

struct MACH0_(obj_t)* MACH0_(new_buf_steal)(RBuffer *buf, bool verbose) {
	struct MACH0_(obj_t) *bin = R_NEW0 (struct MACH0_(obj_t));
	if (!bin) {
		return NULL;
	}
	bin->kv = sdb_new (NULL, "bin.mach0", 0);
	bin->size = r_buf_size (buf);
	bin->verbose = verbose;
	bin->b = buf;
	if (!init (bin)) {
		return MACH0_(mach0_free)(bin);
	}
	return bin;
}

Project: radare2
File: mach0.c

int MACH0_(get_bits)(struct MACH0_(obj_t)* bin) {
	if (bin) {
		int bits = MACH0_(get_bits_from_hdr) (&bin->hdr);
		if (bin->hdr.cputype == CPU_TYPE_ARM && bin->entry & 1) {
			return 16;
		}
		return bits;
	}
	return 32;
}

Project: radare2
File: mach0.c

const char* MACH0_(get_cputype_from_hdr)(struct MACH0_(mach_header) *hdr) {
	const char *archstr = "unknown";
	switch (hdr->cputype) {
	case CPU_TYPE_VAX:
		archstr = "vax";
		break;
	case CPU_TYPE_MC680x0:
		archstr = "mc680x0";
		break;
	case CPU_TYPE_I386:
	case CPU_TYPE_X86_64:
		archstr = "x86";
		break;
	case CPU_TYPE_MC88000:
		archstr = "mc88000";
		break;
	case CPU_TYPE_MC98000:
		archstr = "mc98000";
		break;
	case CPU_TYPE_HPPA:
		archstr = "hppa";
		break;
	case CPU_TYPE_ARM:
	case CPU_TYPE_ARM64:
		archstr = "arm";
		break;
	case CPU_TYPE_SPARC:
		archstr = "sparc";
		break;
	case CPU_TYPE_MIPS:
		archstr = "mips";
		break;
	case CPU_TYPE_I860:
		archstr = "i860";
		break;
	case CPU_TYPE_POWERPC:
	case CPU_TYPE_POWERPC64:
		archstr = "ppc";
	}
	return archstr;
}

Project: radare2

struct addr_t* MACH0_(get_entrypoint)(struct MACH0_(obj_t)* bin) {
	struct addr_t *entry;
	int i;

	if (!bin->entry && !bin->sects) {
		return NULL;
	}
	if (!(entry = calloc (1, sizeof (struct addr_t)))) {
		return NULL;
	}
	if (bin->entry) {
		entry->addr = entry_to_vaddr (bin);
		entry->offset = addr_to_offset (bin, entry->addr);
		entry->haddr = sdb_num_get (bin->kv, "mach0.entry.offset", 0);
		sdb_num_set (bin->kv, "mach0.entry.vaddr", entry->addr, 0);
		sdb_num_set (bin->kv, "mach0.entry.paddr", bin->entry, 0);
	}
	if (!bin->entry || entry->offset == 0) {
		for (i = 0; i < bin->nsects; i++) {
			if (!strncmp (bin->sects[i].sectname, "__text", 6)) {
				entry->offset = (ut64)bin->sects[i].offset;
				sdb_num_set (bin->kv, "mach0.entry", entry->offset, 0);
				entry->addr = (ut64)bin->sects[i].addr;
				if (!entry->addr) { // workaround for object files
					entry->addr = entry->offset;
				}
				break;
			}
		}
		bin->entry = entry->addr;
	}
	return entry;
}

Project: radare2
File: mach0.c

struct lib_t* MACH0_(get_libs)(struct MACH0_(obj_t)* bin) {
	struct lib_t *libs;
	int i;

	if (!bin->nlibs) {
		return NULL;
	}
	if (!(libs = calloc ((bin->nlibs + 1), sizeof (struct lib_t)))) {
		return NULL;
	}
	for (i = 0; i < bin->nlibs; i++) {
		sdb_set (bin->kv, sdb_fmt ("libs.%d.name", i), bin->libs[i], 0);
		strncpy (libs[i].name, bin->libs[i], R_BIN_MACH0_STRING_LENGTH);
		libs[i].name[R_BIN_MACH0_STRING_LENGTH-1] = '\0';
		libs[i].last = 0;
	}
	libs[i].last = 1;
	return libs;
}

Project: radare2

ut64 MACH0_(get_main)(struct MACH0_(obj_t)* bin) {
	ut64 addr = 0LL;
	struct symbol_t *symbols;
	int i;

	if (!(symbols = MACH0_(get_symbols) (bin))) {
		return 0;
	}
	for (i = 0; !symbols[i].last; i++) {
		const char *name = symbols[i].name;
		if (!strcmp (name, "__Dmain")) {
			addr = symbols[i].addr;
			break;
		}
		if (strstr (name, "4main") && !strstr (name, "STATIC")) {
			addr = symbols[i].addr;
			break;
		}
		if (!strcmp (symbols[i].name, "_main")) {
			addr = symbols[i].addr;
		}
	}
	free (symbols);

	if (!addr && bin->main_cmd.cmd == LC_MAIN) {
		addr = bin->entry + bin->baddr;
	}

	if (!addr) {
		ut8 b[128];
		ut64 entry = addr_to_offset(bin, bin->entry);
		if (entry > bin->size || entry + sizeof (b) > bin->size) {
			return 0;
		}
		i = r_buf_read_at (bin->b, entry, b, sizeof (b));
		if (i < 1) {
			return 0;
		}
		for (i = 0; i < 64; i++) {
			if (b[i] == 0xe8 && !b[i+3] && !b[i+4]) {
				int delta = b[i+1] | (b[i+2] << 8) | (b[i+3] << 16) | (b[i+4] << 24);
				return bin->entry + i + 5 + delta;

			}
		}
	}
	return addr;
}

Project: radare2
File: mach0.c

const char* MACH0_(get_os)(struct MACH0_(obj_t)* bin) {
	if (bin)
	switch (bin->os) {
	case 1: return "macos";
	case 2: return "ios";
	case 3: return "watchos";
	case 4: return "tvos";
	}
	return "darwin";
}

Project: radare2

struct reloc_t* MACH0_(get_relocs)(struct MACH0_(obj_t)* bin) {
	struct reloc_t *relocs;
	int i = 0, len;
	ulebr ur = {NULL};
	int wordsize = MACH0_(get_bits)(bin) / 8;
	if (bin->dyld_info) {
		ut8 *opcodes,*end, type = 0, rel_type = 0;
		int lib_ord, seg_idx = -1, sym_ord = -1;
		size_t j, count, skip, bind_size, lazy_size;
		st64 addend = 0;
		ut64 segmentAddress = 0LL;
		ut64 addr = 0LL;
		ut8 done = 0;

#define CASE(T) case (T / 8): rel_type = R_BIN_RELOC_ ## T; break
		switch (wordsize) {
		CASE(8);
		CASE(16);
		CASE(32);
		CASE(64);
		default: return NULL;
		}
#undef CASE
		bind_size = bin->dyld_info->bind_size;
		lazy_size = bin->dyld_info->lazy_bind_size;

		if (!bind_size || !lazy_size) {
			return NULL;
		}

		if ((bind_size + lazy_size)<1) {
			return NULL;
		}
		if (bin->dyld_info->bind_off > bin->size || bin->dyld_info->bind_off + bind_size > bin->size) {
			return NULL;
		}
		if (bin->dyld_info->lazy_bind_off > bin->size || \
			bin->dyld_info->lazy_bind_off + lazy_size > bin->size) {
			return NULL;
		}
		if (bin->dyld_info->bind_off+bind_size+lazy_size > bin->size) {
			return NULL;
		}
		if (!(relocs = calloc (1, (1 + bind_size + lazy_size) * sizeof (struct reloc_t)))) {
			return NULL;
		}
		opcodes = calloc (1, bind_size + lazy_size + 1);
		if (!opcodes) {
			free (relocs);
			return NULL;
		}
		len = r_buf_read_at (bin->b, bin->dyld_info->bind_off, opcodes, bind_size);
		i = r_buf_read_at (bin->b, bin->dyld_info->lazy_bind_off, opcodes + bind_size, lazy_size);
		if (len < 1 || i < 1) {
			bprintf ("Error: read (dyld_info bind) at 0x%08"PFMT64x"\n",
			(ut64)(size_t)bin->dyld_info->bind_off);
			free (opcodes);
			relocs[i].last = 1;
			return relocs;
		}
		i = 0;
		for (ur.p = opcodes, end = opcodes + bind_size + lazy_size ; (ur.p+2 < end) && !done; ) {
			ut8 imm = *ur.p & BIND_IMMEDIATE_MASK, op = *ur.p & BIND_OPCODE_MASK;
			++ur.p;
			switch (op) {
#define ULEB() read_uleb128 (&ur,end)
#define SLEB() read_sleb128 (&ur,end)
			case BIND_OPCODE_DONE:
				done = 1;
				break;
			case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
				lib_ord = imm;
				break;
			case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
				lib_ord = ULEB();
				break;
			case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
				lib_ord = imm? (st8)(BIND_OPCODE_MASK | imm) : 0;
				break;
			case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: {
				char *sym_name = (char*)ur.p;
				while (*ur.p++ && ur.p<end) {
					/* empty loop */
				}
				sym_ord = -1;
				if (bin->symtab && bin->dysymtab.nundefsym < 0xffff)
				for (j = 0; j < bin->dysymtab.nundefsym; j++) {
					int stridx = 0;
					int iundefsym = bin->dysymtab.iundefsym;
					if (iundefsym>=0 && iundefsym < bin->nsymtab) {
						int sidx = iundefsym +j;
						if (sidx<0 || sidx>= bin->nsymtab)
							continue;
						stridx = bin->symtab[sidx].n_strx;
						if (stridx < 0 || stridx >= bin->symstrlen)
							continue;
					}
					if (!strcmp ((char *)bin->symstr + stridx, sym_name)) {
						sym_ord = j;
						break;
					}
				}
				break;
			}
			case BIND_OPCODE_SET_TYPE_IMM:
				type = imm;
				break;
			case BIND_OPCODE_SET_ADDEND_SLEB:
				addend = SLEB();
				break;
			case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
				seg_idx = imm;
				if (seg_idx < 0 || seg_idx >= bin->nsegs) {
					bprintf ("Error: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"
						" has unexistent segment %d\n", seg_idx);
					addr = 0LL;
					return 0; // early exit to avoid future mayhem
				} else {
					addr = bin->segs[seg_idx].vmaddr + ULEB();
					segmentAddress = bin->segs[seg_idx].vmaddr \
							+ bin->segs[seg_idx].vmsize;
				}
				break;
			case BIND_OPCODE_ADD_ADDR_ULEB:
				addr += ULEB();
				break;
#define DO_BIND() do {\
if (sym_ord < 0 || seg_idx < 0 ) break;\
if (i >= (bind_size + lazy_size)) break;\
relocs[i].addr = addr;\
relocs[i].offset = addr - bin->segs[seg_idx].vmaddr + bin->segs[seg_idx].fileoff;\
if (type == BIND_TYPE_TEXT_PCREL32)\
	relocs[i].addend = addend - (bin->baddr + addr);\
else relocs[i].addend = addend;\
/* library ordinal ??? */ \
relocs[i].ord = lib_ord;\
relocs[i].ord = sym_ord;\
relocs[i].type = rel_type;\
relocs[i++].last = 0;\
} while (0)
			case BIND_OPCODE_DO_BIND:
				if (addr >= segmentAddress) {
					bprintf ("Error: Malformed DO bind opcode\n");
					goto beach;
				}
				DO_BIND();
				addr += wordsize;
				break;
			case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
				if (addr >= segmentAddress) {
					bprintf ("Error: Malformed ADDR ULEB bind opcode\n");
					goto beach;
				}
				DO_BIND();
				addr += ULEB() + wordsize;
				break;
			case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
				if (addr >= segmentAddress) {
					bprintf ("Error: Malformed IMM SCALED bind opcode\n");
					goto beach;
				}
				DO_BIND();
				addr += (ut64)imm * (ut64)wordsize + wordsize;
				break;
			case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
				count = ULEB();
				skip = ULEB();
				for (j = 0; j < count; j++) {
					if (addr >= segmentAddress) {
						bprintf ("Error: Malformed ULEB TIMES bind opcode\n");
						goto beach;
					}
					DO_BIND();
					addr += skip + wordsize;
				}
				break;
#undef DO_BIND
#undef ULEB
#undef SLEB
			default:
				bprintf ("Error: unknown bind opcode 0x%02x in dyld_info\n", *ur.p);
				free (opcodes);
				relocs[i].last = 1;
				return relocs;
			}
		}
		free (opcodes);
	} else {
		int j;
		if (!bin->symtab || !bin->symstr || !bin->sects || !bin->indirectsyms) {
			return NULL;
		}
		if (!(relocs = malloc ((bin->dysymtab.nundefsym + 1) * sizeof (struct reloc_t)))) {
			return NULL;
		}
		for (j = 0; j < bin->dysymtab.nundefsym; j++) {
			if (parse_import_ptr (bin, &relocs[i], bin->dysymtab.iundefsym + j)) {
				relocs[i].ord = j;
				relocs[i++].last = 0;
			}
		}
	}
beach:
	relocs[i].last = 1;
	return relocs;
}

Project: radare2
File: mach0.c

static int inSymtab(SdbHash *hash, struct symbol_t *symbols, const char *name, ut64 addr) {
	bool found;
	const char *key = sdb_fmt ("%s.%"PFMT64x, name, addr);
	(void)sdb_ht_find (hash, key, &found);
	if (found) {
		return true;
	}
	sdb_ht_insert (hash, key, "1");
	return false;
}

Project: radare2

static int init_items(struct MACH0_(obj_t)* bin) {
	struct load_command lc = {0, 0};
	ut8 loadc[sizeof (struct load_command)] = {0};
	bool is_first_thread = true;
	ut64 off = 0LL;
	int i, len;

	bin->uuidn = 0;
	bin->os = 0;
	bin->has_crypto = 0;
	if (bin->hdr.sizeofcmds > bin->size) {
		bprintf ("Warning: chopping hdr.sizeofcmds\n");
		bin->hdr.sizeofcmds = bin->size - 128;
	}
	for (i = 0, off = sizeof (struct MACH0_(mach_header)); \
			i < bin->hdr.ncmds; i++, off += lc.cmdsize) {
		if (off > bin->size || off + sizeof (struct load_command) > bin->size){
			bprintf ("mach0: out of bounds command\n");
			return false;
		}
		len = r_buf_read_at (bin->b, off, loadc, sizeof (struct load_command));
		if (len < 1) {
			bprintf ("Error: read (lc) at 0x%08"PFMT64x"\n", off);
			return false;
		}
		lc.cmd = r_read_ble32 (&loadc[0], bin->big_endian);
		lc.cmdsize = r_read_ble32 (&loadc[4], bin->big_endian);

		if (lc.cmdsize < 1 || off + lc.cmdsize > bin->size) {
			bprintf ("Warning: mach0_header %d = cmdsize<1. (0x%llx vs 0x%llx)\n", i,
				(ut64)(off + lc.cmdsize), (ut64)(bin->size));
			break;
		}

		sdb_num_set (bin->kv, sdb_fmt ("mach0_cmd_%d.offset", i), off, 0);
		sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.format", i), "xd cmd size", 0);

		switch (lc.cmd) {
		case LC_DATA_IN_CODE:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "data_in_code", 0);
			break;
		case LC_RPATH:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "rpath", 0);
			break;
		case LC_SEGMENT_64:
		case LC_SEGMENT:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "segment", 0);
			bin->nsegs++;
			if (!parse_segments (bin, off)) {
				bprintf ("error parsing segment\n");
				bin->nsegs--;
				return false;
			}
			break;
		case LC_SYMTAB:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "symtab", 0);
			if (!parse_symtab (bin, off)) {
				bprintf ("error parsing symtab\n");
				return false;
			}
			break;
		case LC_DYSYMTAB:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "dysymtab", 0);
			if (!parse_dysymtab(bin, off)) {
				bprintf ("error parsing dysymtab\n");
				return false;
			}
			break;
		case LC_DYLIB_CODE_SIGN_DRS:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "dylib_code_sign_drs", 0);
			break;
		case LC_VERSION_MIN_MACOSX:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "version_min_macosx", 0);
			bin->os = 1;
			break;
		case LC_VERSION_MIN_IPHONEOS:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "version_min_iphoneos", 0);
			bin->os = 2;
			break;
		case LC_VERSION_MIN_TVOS:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "version_min_tvos", 0);
			bin->os = 4;
			break;
		case LC_VERSION_MIN_WATCHOS:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "version_min_watchos", 0);
			bin->os = 3;
			break;
		case LC_UUID:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "uuid", 0);
			{
			struct uuid_command uc = {0};
			if (off + sizeof (struct uuid_command) > bin->size) {
				bprintf ("UUID out of obunds\n");
				return false;
			}
			if (r_buf_fread_at (bin->b, off, (ut8*)&uc, "24c", 1) != -1) {
				char key[128];
				char val[128];
				snprintf (key, sizeof (key)-1, "uuid.%d", bin->uuidn++);
				r_hex_bin2str ((ut8*)&uc.uuid, 16, val);
				sdb_set (bin->kv, key, val, 0);
			}
			}
			break;
		case LC_ENCRYPTION_INFO_64:
			/* TODO: the struct is probably different here */
		case LC_ENCRYPTION_INFO:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "encryption_info", 0);
			{
			struct MACH0_(encryption_info_command) eic = {0};
			ut8 seic[sizeof (struct MACH0_(encryption_info_command))] = {0};
			if (off + sizeof (struct MACH0_(encryption_info_command)) > bin->size) {
				bprintf ("encryption info out of bounds\n");
				return false;
			}
			if (r_buf_read_at (bin->b, off, seic, sizeof (struct MACH0_(encryption_info_command))) != -1) {
				eic.cmd = r_read_ble32 (&seic[0], bin->big_endian);
				eic.cmdsize = r_read_ble32 (&seic[4], bin->big_endian);
				eic.cryptoff = r_read_ble32 (&seic[8], bin->big_endian);
				eic.cryptsize = r_read_ble32 (&seic[12], bin->big_endian);
				eic.cryptid = r_read_ble32 (&seic[16], bin->big_endian);

				bin->has_crypto = eic.cryptid;
				sdb_set (bin->kv, "crypto", "true", 0);
				sdb_num_set (bin->kv, "cryptid", eic.cryptid, 0);
				sdb_num_set (bin->kv, "cryptoff", eic.cryptoff, 0);
				sdb_num_set (bin->kv, "cryptsize", eic.cryptsize, 0);
				sdb_num_set (bin->kv, "cryptheader", off, 0);
			} }
			break;
		case LC_LOAD_DYLINKER:
			{
				sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "dylinker", 0);
				free (bin->intrp);
				bin->intrp = NULL;
				struct dylinker_command dy = {0};
				ut8 sdy[sizeof (struct dylinker_command)] = {0};
				if (off + sizeof (struct dylinker_command) > bin->size){
					bprintf ("Warning: Cannot parse dylinker command\n");
					return false;
				}
				if (r_buf_read_at (bin->b, off, sdy, sizeof (struct dylinker_command)) == -1) {
					bprintf ("Warning: read (LC_DYLD_INFO) at 0x%08"PFMT64x"\n", off);
				} else {
					dy.cmd = r_read_ble32 (&sdy[0], bin->big_endian);
					dy.cmdsize = r_read_ble32 (&sdy[4], bin->big_endian);
					dy.name = r_read_ble32 (&sdy[8], bin->big_endian);

					int len = dy.cmdsize;
					char *buf = malloc (len+1);
					if (buf) {
						r_buf_read_at (bin->b, off + 0xc, (ut8*)buf, len);
						buf[len] = 0;
						free (bin->intrp);
						bin->intrp = buf;
					}
				}
			}
			break;
		case LC_MAIN:
			{
			struct {
				ut64 eo;
				ut64 ss;
			} ep = {0};
			ut8 sep[2 * sizeof (ut64)] = {0};
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "main", 0);

			if (!is_first_thread) {
				bprintf("Error: LC_MAIN with other threads\n");
				return false;
			}
			if (off + 8 > bin->size || off + sizeof (ep) > bin->size) {
				bprintf ("invalid command size for main\n");
				return false;
			}
			r_buf_read_at (bin->b, off + 8, sep, 2 * sizeof (ut64));
			ep.eo = r_read_ble64 (&sep[0], bin->big_endian);
			ep.ss = r_read_ble64 (&sep[8], bin->big_endian);

			bin->entry = ep.eo;
			bin->main_cmd = lc;

			sdb_num_set (bin->kv, "mach0.entry.offset", off + 8, 0);
			sdb_num_set (bin->kv, "stacksize", ep.ss, 0);

			is_first_thread = false;
			}
			break;
		case LC_UNIXTHREAD:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "unixthread", 0);
			if (!is_first_thread) {
				bprintf("Error: LC_UNIXTHREAD with other threads\n");
				return false;
			}
		case LC_THREAD:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "thread", 0);
			if (!parse_thread (bin, &lc, off, is_first_thread)) {
				bprintf ("Cannot parse thread\n");
				return false;
			}
			is_first_thread = false;
			break;
		case LC_LOAD_DYLIB:
		case LC_LOAD_WEAK_DYLIB:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "load_dylib", 0);
			bin->nlibs++;
			if (!parse_dylib (bin, off)){
				bprintf ("Cannot parse dylib\n");
				bin->nlibs--;
				return false;
			}
			break;
		case LC_DYLD_INFO:
		case LC_DYLD_INFO_ONLY:
			{
			ut8 dyldi[sizeof (struct dyld_info_command)] = {0};
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "dyld_info", 0);
			bin->dyld_info = calloc (1, sizeof (struct dyld_info_command));
			if (bin->dyld_info) {
				if (off + sizeof (struct dyld_info_command) > bin->size){
					bprintf ("Cannot parse dyldinfo\n");
					R_FREE (bin->dyld_info);
					return false;
				}
				if (r_buf_read_at (bin->b, off, dyldi, sizeof (struct dyld_info_command)) == -1) {
					free (bin->dyld_info);
					bin->dyld_info = NULL;
					bprintf ("Error: read (LC_DYLD_INFO) at 0x%08"PFMT64x"\n", off);
				} else {
					bin->dyld_info->cmd = r_read_ble32 (&dyldi[0], bin->big_endian);
					bin->dyld_info->cmdsize = r_read_ble32 (&dyldi[4], bin->big_endian);
					bin->dyld_info->rebase_off = r_read_ble32 (&dyldi[8], bin->big_endian);
					bin->dyld_info->rebase_size = r_read_ble32 (&dyldi[12], bin->big_endian);
					bin->dyld_info->bind_off = r_read_ble32 (&dyldi[16], bin->big_endian);
					bin->dyld_info->bind_size = r_read_ble32 (&dyldi[20], bin->big_endian);
					bin->dyld_info->weak_bind_off = r_read_ble32 (&dyldi[24], bin->big_endian);
					bin->dyld_info->weak_bind_size = r_read_ble32 (&dyldi[28], bin->big_endian);
					bin->dyld_info->lazy_bind_off = r_read_ble32 (&dyldi[32], bin->big_endian);
					bin->dyld_info->lazy_bind_size = r_read_ble32 (&dyldi[36], bin->big_endian);
					bin->dyld_info->export_off = r_read_ble32 (&dyldi[40], bin->big_endian);
					bin->dyld_info->export_size = r_read_ble32 (&dyldi[44], bin->big_endian);
				}
			}
			}
			break;
		case LC_CODE_SIGNATURE:
			parse_signature (bin, off);
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "signature", 0);
			/* ut32 dataoff
			break;
		case LC_SOURCE_VERSION:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "version", 0);
			/* uint64_t  version;  */
			/* A.B.C.D.E packed as a24.b10.c10.d10.e10 */
			break;
		case LC_SEGMENT_SPLIT_INFO:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "split_info", 0);
			/* TODO */
			break;
		case LC_FUNCTION_STARTS:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "function_starts", 0);
			if (!parse_function_starts (bin, off)) {
				bprintf ("Cannot parse LC_FUNCTION_STARTS\n");
			}
			break;
		case LC_REEXPORT_DYLIB:
			sdb_set (bin->kv, sdb_fmt ("mach0_cmd_%d.cmd", i), "dylib", 0);
			/* TODO */
			break;
		default:
			break;
		}
	}
	return true;
}

Project: radare2
File: mach0.c

void MACH0_(kv_loadlibs)(struct MACH0_(obj_t)* bin) {
	int i;
	for (i = 0; i < bin->nlibs; i++) {
		sdb_set (bin->kv, sdb_fmt ("libs.%d.name", i), bin->libs[i], 0);
	}
}

Project: radare2
File: mach0.c

RList* MACH0_(mach_fields)(RBinFile *bf) {
	struct MACH0_(mach_header) *mh = MACH0_(get_hdr_from_bytes)(bf->buf);
	if (!mh) {
		return NULL;
	}
	RList *ret = r_list_new ();
	if (!ret) {
		return NULL;
	}
	ret->free = free;
	ut64 addr = 0;

#define ROW(nam,siz,val,fmt) \
	r_list_append (ret, r_bin_field_new (addr, addr, siz, nam, sdb_fmt ("0x%08x", val), fmt)); \
	addr += 4;
	ROW("hdr.magic", 4, mh->magic, "x");
	ROW("hdr.cputype", 4, mh->cputype, NULL);
	ROW("hdr.cpusubtype", 4, mh->cpusubtype, NULL);
	ROW("hdr.filetype", 4, mh->filetype, NULL);
	ROW("hdr.ncmds", 4, mh->ncmds, NULL);
	ROW("hdr.sizeofcmds", 4, mh->sizeofcmds, NULL);
	free (mh);
	return ret;
}

Project: radare2
File: mach0.c

void MACH0_(mach_headerfields)(RBinFile *file) {
	RBuffer *buf = file->buf;
	int n = 0;
	struct MACH0_(mach_header) *mh = MACH0_(get_hdr_from_bytes)(buf);
	if (!mh) {
		return;
	}
	printf ("0x00000000  Magic       0x%x\n", mh->magic);
	printf ("0x00000004  CpuType     0x%x\n", mh->cputype);
	printf ("0x00000008  CpuSubType  0x%x\n", mh->cpusubtype);
	printf ("0x0000000c  FileType    0x%x\n", mh->filetype);
	printf ("0x00000010  nCmds       %d\n", mh->ncmds);
	printf ("0x00000014  sizeOfCmds  %d\n", mh->sizeofcmds);
	printf ("0x00000018  Flags       0x%x\n", mh->flags);

	ut64 addr = 0x20 - 4;
	ut32 word = 0;
	ut8 wordbuf[sizeof (word)];
#define READWORD() \
		addr += 4; \
		if (!r_buf_read_at (buf, addr, (ut8*)wordbuf, 4)) { \
			eprintf ("Invalid address in buffer."); \
			break; \
		} \
		word = r_read_le32 (wordbuf);
	for (n = 0; n < mh->ncmds; n++) {
		printf ("\n# Load Command %d\n", n);
		READWORD();
		int lcType = word;
		eprintf ("0x%08"PFMT64x"  cmd          0x%x %s\n",
			addr, lcType, cmd_to_string (lcType));
		READWORD();
		int lcSize = word;
		word &= 0xFFFFFF;
		printf ("0x%08"PFMT64x"  cmdsize      %d\n", addr, word);
		if (lcSize < 1) {
			eprintf ("Invalid size for a load command\n");
			break;
		}
		switch (lcType) {
		case LC_ID_DYLIB: // install_name_tool
			printf ("0x%08"PFMT64x"  id           %s\n",
				addr + 20, r_buf_get_at (buf, addr + 20, NULL));
			break;
		case LC_UUID:
			printf ("0x%08"PFMT64x"  uuid         %s\n",
				addr + 20, r_buf_get_at (buf, addr + 32, NULL));
			break;
		case LC_LOAD_DYLIB:
			printf ("0x%08"PFMT64x"  uuid         %s\n",
				addr + 20, r_buf_get_at (buf, addr + 20, NULL));
			break;
		case LC_RPATH:
			printf ("0x%08"PFMT64x"  uuid         %s\n",
				addr + 8, r_buf_get_at (buf, addr + 8, NULL));
			break;
		case LC_CODE_SIGNATURE:
			{
			ut32 *words = (ut32*)r_buf_get_at (buf, addr + 4, NULL);
			printf ("0x%08"PFMT64x"  dataoff      0x%08x\n", addr + 4, words[0]);
			printf ("0x%08"PFMT64x"  datasize     %d\n", addr + 8, words[1]);
			printf ("# wtf mach0.sign %d @ 0x%x\n", words[1], words[0]);
			}
			break;
		}
		addr += word - 8;
	}
	free (mh);
}

Project: radare2

static int parse_import_stub(struct MACH0_(obj_t)* bin, struct symbol_t *symbol, int idx) {
	int i, j, nsyms, stridx;
	const char *symstr;
	if (idx < 0) {
		return 0;
	}
	symbol->offset = 0LL;
	symbol->addr = 0LL;
	symbol->name[0] = '\0';

	if (!bin || !bin->sects) {
		return false;
	}
	for (i = 0; i < bin->nsects; i++) {
		if ((bin->sects[i].flags & SECTION_TYPE) == S_SYMBOL_STUBS && bin->sects[i].reserved2 > 0) {
			nsyms = (int)(bin->sects[i].size / bin->sects[i].reserved2);
			if (nsyms > bin->size) {
				bprintf ("mach0: Invalid symbol table size\n");
			}
			for (j = 0; j < nsyms; j++) {
				if (bin->sects) {
					if (bin->sects[i].reserved1 + j >= bin->nindirectsyms) {
						continue;
					}
				}
				if (bin->indirectsyms) {
					if (idx != bin->indirectsyms[bin->sects[i].reserved1 + j]) {
						continue;
					}
				}
				if (idx > bin->nsymtab) {
					continue;
				}
				symbol->type = R_BIN_MACH0_SYMBOL_TYPE_LOCAL;
				symbol->offset = bin->sects[i].offset + j * bin->sects[i].reserved2;
				symbol->addr = bin->sects[i].addr + j * bin->sects[i].reserved2;
				symbol->size = 0;
				stridx = bin->symtab[idx].n_strx;
				if (stridx >= 0 && stridx < bin->symstrlen) {
					symstr = (char *)bin->symstr + stridx;
				} else {
					symstr = "???";
				}
				if (*symstr == '_') {
					symstr++;
				}
				snprintf (symbol->name, R_BIN_MACH0_STRING_LENGTH, "imp.%s", symstr);
				return true;
			}
		}
	}
	return false;
}

Project: radare2
File: mach0.c

static int parse_segments(struct MACH0_(obj_t)* bin, ut64 off) {
	int i, j, k, sect, len;
	ut32 size_sects;
	ut8 segcom[sizeof (struct MACH0_(segment_command))] = {0};
	ut8 sec[sizeof (struct MACH0_(section))] = {0};

	if (!UT32_MUL (&size_sects, bin->nsegs, sizeof (struct MACH0_(segment_command)))) {
		return false;
	}
	if (!size_sects || size_sects > bin->size) {
		return false;
	}
	if (off > bin->size || off + sizeof (struct MACH0_(segment_command)) > bin->size) {
		return false;
	}
	if (!(bin->segs = realloc (bin->segs, bin->nsegs * sizeof(struct MACH0_(segment_command))))) {
		perror ("realloc (seg)");
		return false;
	}
	j = bin->nsegs - 1;
	len = r_buf_read_at (bin->b, off, segcom, sizeof (struct MACH0_(segment_command)));
	if (len != sizeof (struct MACH0_(segment_command))) {
		bprintf ("Error: read (seg)\n");
		return false;
	}
	i = 0;
	bin->segs[j].cmd = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].cmdsize = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	memcpy (&bin->segs[j].segname, &segcom[i], 16);
	i += 16;
#if R_BIN_MACH064
	bin->segs[j].vmaddr = r_read_ble64 (&segcom[i], bin->big_endian);
	i += sizeof (ut64);
	bin->segs[j].vmsize = r_read_ble64 (&segcom[i], bin->big_endian);
	i += sizeof (ut64);
	bin->segs[j].fileoff = r_read_ble64 (&segcom[i], bin->big_endian);
	i += sizeof (ut64);
	bin->segs[j].filesize = r_read_ble64 (&segcom[i], bin->big_endian);
	i += sizeof (ut64);
#else
	bin->segs[j].vmaddr = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].vmsize = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].fileoff = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].filesize = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
#endif
	bin->segs[j].maxprot = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].initprot = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].nsects = r_read_ble32 (&segcom[i], bin->big_endian);
	i += sizeof (ut32);
	bin->segs[j].flags = r_read_ble32 (&segcom[i], bin->big_endian);

	sdb_num_set (bin->kv, sdb_fmt ("mach0_segment_%d.offset", j), off, 0);
	sdb_num_set (bin->kv, "mach0_segments.count", 0, 0);
	sdb_set (bin->kv, "mach0_segment.format",
		"xd[16]zxxxxoodx "
		"cmd cmdsize segname vmaddr vmsize "
		"fileoff filesize maxprot initprot nsects flags", 0);

	if (bin->segs[j].nsects > 0) {
		sect = bin->nsects;
		bin->nsects += bin->segs[j].nsects;
		if (bin->nsects > 128) {
			int new_nsects = bin->nsects & 0xf;
			bprintf ("WARNING: mach0 header contains too many sections (%d). Wrapping to %d\n",
				 bin->nsects, new_nsects);
			bin->nsects = new_nsects;
		}
		if ((int)bin->nsects < 1) {
			bprintf ("Warning: Invalid number of sections\n");
			bin->nsects = sect;
			return false;
		}
		if (!UT32_MUL (&size_sects, bin->nsects-sect, sizeof (struct MACH0_(section)))){
			bin->nsects = sect;
			return false;
		}
		if (!size_sects || size_sects > bin->size){
			bin->nsects = sect;
			return false;
		}

		if (bin->segs[j].cmdsize != sizeof (struct MACH0_(segment_command)) \
				  + (sizeof (struct MACH0_(section))*bin->segs[j].nsects)){
			bin->nsects = sect;
			return false;
		}

		if (off + sizeof (struct MACH0_(segment_command)) > bin->size ||\
				off + sizeof (struct MACH0_(segment_command)) + size_sects > bin->size){
			bin->nsects = sect;
			return false;
		}

		if (!(bin->sects = realloc (bin->sects, bin->nsects * sizeof (struct MACH0_(section))))) {
			perror ("realloc (sects)");
			bin->nsects = sect;
			return false;
		}

		for (k = sect, j = 0; k < bin->nsects; k++, j++) {
			ut64 offset = off + sizeof (struct MACH0_(segment_command)) + j * sizeof (struct MACH0_(section));
			len = r_buf_read_at (bin->b, offset, sec, sizeof (struct MACH0_(section)));
			if (len != sizeof (struct MACH0_(section))) {
				bprintf ("Error: read (sects)\n");
				bin->nsects = sect;
				return false;
			}

			i = 0;
			memcpy (&bin->sects[k].sectname, &sec[i], 16);
			i += 16;
			memcpy (&bin->sects[k].segname, &sec[i], 16);
			bin->sects[k].segname[15] = 0;
			i += 16;
#if R_BIN_MACH064
			bin->sects[k].addr = r_read_ble64 (&sec[i], bin->big_endian);
			i += sizeof (ut64);
			bin->sects[k].size = r_read_ble64 (&sec[i], bin->big_endian);
			i += sizeof (ut64);
#else
			bin->sects[k].addr = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].size = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
#endif
			bin->sects[k].offset = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].align = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].reloff = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].nreloc = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].flags = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].reserved1 = r_read_ble32 (&sec[i], bin->big_endian);
			i += sizeof (ut32);
			bin->sects[k].reserved2 = r_read_ble32 (&sec[i], bin->big_endian);
#if R_BIN_MACH064
			i += sizeof (ut32);
			bin->sects[k].reserved3 = r_read_ble32 (&sec[i], bin->big_endian);
#endif
		}
	}
	return true;
}

Project: radare2
File: mach0.c

static bool parse_signature(struct MACH0_(obj_t) *bin, ut64 off) {
	int i,len;
	ut32 data;
	bin->signature = NULL;
	struct linkedit_data_command link = {0};
	ut8 lit[sizeof (struct linkedit_data_command)] = {0};
	struct blob_index_t idx = {0};
	struct super_blob_t super = {{0}};

	if (off > bin->size || off + sizeof (struct linkedit_data_command) > bin->size) {
		return false;
	}
	len = r_buf_read_at (bin->b, off, lit, sizeof (struct linkedit_data_command));
	if (len != sizeof (struct linkedit_data_command)) {
		bprintf ("Failed to get data while parsing LC_CODE_SIGNATURE command\n");
		return false;
	}
	link.cmd = r_read_ble32 (&lit[0], bin->big_endian);
	link.cmdsize = r_read_ble32 (&lit[4], bin->big_endian);
	link.dataoff = r_read_ble32 (&lit[8], bin->big_endian);
	link.datasize = r_read_ble32 (&lit[12], bin->big_endian);

	data = link.dataoff;
	if (data > bin->size || data + sizeof (struct super_blob_t) > bin->size) {
		bin->signature = (ut8 *)strdup ("Malformed entitlement");
		return true;
	}
	super.blob.magic = r_read_ble32 (bin->b->buf + data, little_);
	super.blob.length = r_read_ble32 (bin->b->buf + data + 4, little_);
	super.count = r_read_ble32 (bin->b->buf + data + 8, little_);
	for (i = 0; i < super.count; ++i) {
		if ((ut8 *)(bin->b->buf + data + i) > (ut8 *)(bin->b->buf + bin->size)) {
			bin->signature = (ut8 *)strdup ("Malformed entitlement");
			break;
		}
		struct blob_index_t bi;
		if (r_buf_read_at (bin->b, data + 12 + (i * sizeof (struct blob_index_t)),
			(ut8*)&bi, sizeof (struct blob_index_t)) < sizeof (struct blob_index_t)) {
			break;
		}
		idx.type = r_read_ble32 (&bi.type, little_);
		idx.offset = r_read_ble32 (&bi.offset, little_);
		if (idx.type == CSSLOT_ENTITLEMENTS) {
			ut64 off = data + idx.offset;
			if (off > bin->size || off + sizeof (struct blob_t) > bin->size) {
				bin->signature = (ut8 *)strdup ("Malformed entitlement");
				break;
			}
			struct blob_t entitlements = {0};
			entitlements.magic = r_read_ble32 (bin->b->buf + off, little_);
			entitlements.length = r_read_ble32 (bin->b->buf + off + 4, little_);
			len = entitlements.length - sizeof (struct blob_t);
			if (len <= bin->size && len > 1) {
				bin->signature = calloc (1, len + 1);
				if (bin->signature) {
					ut8 *src = bin->b->buf + off + sizeof (struct blob_t);
					if (off + sizeof (struct blob_t) + len < bin->b->length) {
						memcpy (bin->signature, src, len);
						bin->signature[len] = '\0';
						return true;
					}
					bin->signature = (ut8 *)strdup ("Malformed entitlement");
					return true;
				}
			} else {
				bin->signature = (ut8 *)strdup ("Malformed entitlement");
			}
		}
	}
	if (!bin->signature) {
		bin->signature = (ut8 *)strdup ("No entitlement found");
	}
	return true;
}

Project: radare2

static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 off, bool is_first_thread) {
	ut64 ptr_thread, pc = UT64_MAX, pc_offset = UT64_MAX;
	ut32 flavor, count;
	ut8 *arw_ptr = NULL;
	int arw_sz, len = 0;
	ut8 thc[sizeof (struct thread_command)] = {0};
	ut8 tmp[4];

	if (off > bin->size || off + sizeof (struct thread_command) > bin->size)
		return false;

	len = r_buf_read_at (bin->b, off, thc, 8);
	if (len < 1) {
		goto wrong_read;
	}
	bin->thread.cmd = r_read_ble32 (&thc[0], bin->big_endian);
	bin->thread.cmdsize = r_read_ble32 (&thc[4], bin->big_endian);
	if (r_buf_read_at (bin->b, off + sizeof (struct thread_command), tmp, 4) < 4) {
		goto wrong_read;
	}
	flavor = r_read_ble32 (tmp, bin->big_endian);
	if (len == -1)
		goto wrong_read;

	if (off + sizeof (struct thread_command) + sizeof (flavor) > bin->size || \
	  off + sizeof (struct thread_command) + sizeof (flavor) + sizeof (ut32) > bin->size)
		return false;

	if (r_buf_read_at (bin->b, off + sizeof (struct thread_command) + sizeof (flavor), tmp, 4) < 4) {
		goto wrong_read;
	}
	count = r_read_ble32 (tmp, bin->big_endian);
	ptr_thread = off + sizeof (struct thread_command) + sizeof (flavor) + sizeof (count);

	if (ptr_thread > bin->size)
		return false;

	switch (bin->hdr.cputype) {
	case CPU_TYPE_I386:
	case CPU_TYPE_X86_64:
		switch (flavor) {
		case X86_THREAD_STATE32:
			if (ptr_thread + sizeof (struct x86_thread_state32) > bin->size)
				return false;
			if ((len = r_buf_fread_at (bin->b, ptr_thread,
				(ut8*)&bin->thread_state.x86_32, "16i", 1)) == -1) {
				bprintf ("Error: read (thread state x86_32)\n");
				return false;
			}
			pc = bin->thread_state.x86_32.eip;
			pc_offset = ptr_thread + r_offsetof(struct x86_thread_state32, eip);
			arw_ptr = (ut8 *)&bin->thread_state.x86_32;
			arw_sz = sizeof (struct x86_thread_state32);
			break;
		case X86_THREAD_STATE64:
			if (ptr_thread + sizeof (struct x86_thread_state64) > bin->size)
				return false;
			if ((len = r_buf_fread_at (bin->b, ptr_thread,
				(ut8*)&bin->thread_state.x86_64, "32l", 1)) == -1) {
				bprintf ("Error: read (thread state x86_64)\n");
				return false;
			}
			pc = bin->thread_state.x86_64.rip;
			pc_offset = ptr_thread + r_offsetof(struct x86_thread_state64, rip);
			arw_ptr = (ut8 *)&bin->thread_state.x86_64;
			arw_sz = sizeof (struct x86_thread_state64);
			break;
		}
		break;
	case CPU_TYPE_POWERPC:
	case CPU_TYPE_POWERPC64:
		if (flavor == X86_THREAD_STATE32) {
			if (ptr_thread + sizeof (struct ppc_thread_state32) > bin->size)
				return false;
			if ((len = r_buf_fread_at (bin->b, ptr_thread,
				(ut8*)&bin->thread_state.ppc_32, bin->big_endian?"40I":"40i", 1)) == -1) {
				bprintf ("Error: read (thread state ppc_32)\n");
				return false;
			}
			pc = bin->thread_state.ppc_32.srr0;
			pc_offset = ptr_thread + r_offsetof(struct ppc_thread_state32, srr0);
			arw_ptr = (ut8 *)&bin->thread_state.ppc_32;
			arw_sz = sizeof (struct ppc_thread_state32);
		} else if (flavor == X86_THREAD_STATE64) {
			if (ptr_thread + sizeof (struct ppc_thread_state64) > bin->size)
				return false;
			if ((len = r_buf_fread_at (bin->b, ptr_thread,
				(ut8*)&bin->thread_state.ppc_64, bin->big_endian?"34LI3LI":"34li3li", 1)) == -1) {
				bprintf ("Error: read (thread state ppc_64)\n");
				return false;
			}
			pc = bin->thread_state.ppc_64.srr0;
			pc_offset = ptr_thread + r_offsetof(struct ppc_thread_state64, srr0);
			arw_ptr = (ut8 *)&bin->thread_state.ppc_64;
			arw_sz = sizeof (struct ppc_thread_state64);
		}
		break;
	case CPU_TYPE_ARM:
		if (ptr_thread + sizeof (struct arm_thread_state32) > bin->size)
			return false;
		if ((len = r_buf_fread_at (bin->b, ptr_thread,
				(ut8*)&bin->thread_state.arm_32, bin->big_endian?"17I":"17i", 1)) == -1) {
			bprintf ("Error: read (thread state arm)\n");
			return false;
		}
		pc = bin->thread_state.arm_32.r15;
		pc_offset = ptr_thread + r_offsetof (struct arm_thread_state32, r15);
		arw_ptr = (ut8 *)&bin->thread_state.arm_32;
		arw_sz = sizeof (struct arm_thread_state32);
		break;
	case CPU_TYPE_ARM64:
		if (ptr_thread + sizeof (struct arm_thread_state64) > bin->size) {
			return false;
		}
		if ((len = r_buf_fread_at(bin->b, ptr_thread,
				(ut8*)&bin->thread_state.arm_64, bin->big_endian?"34LI1I":"34Li1i", 1)) == -1) {
			bprintf ("Error: read (thread state arm)\n");
			return false;
		}
		pc = r_read_be64 (&bin->thread_state.arm_64.pc);
		pc_offset = ptr_thread + r_offsetof (struct arm_thread_state64, pc);
		arw_ptr = (ut8*)&bin->thread_state.arm_64;
		arw_sz = sizeof (struct arm_thread_state64);
		break;
	default:
		bprintf ("Error: read (unknown thread state structure)\n");
		return false;
	}

	if (arw_ptr && arw_sz > 0) {
		int i;
		ut8 *p = arw_ptr;
		bprintf ("arw ");
		for (i = 0; i < arw_sz; i++) {
			bprintf ("%02x", 0xff & p[i]);
		}
		bprintf ("\n");
	}

	if (is_first_thread) {
		bin->main_cmd = *lc;
		if (pc != UT64_MAX) {
			bin->entry = pc;
		}
		if (pc_offset != UT64_MAX) {
			sdb_num_set (bin->kv, "mach0.entry.offset", pc_offset, 0);
		}
	}

	return true;
wrong_read:
	bprintf ("Error: read (thread)\n");
	return false;
}