#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <mach/machine.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <architecture/byte_order.h>
struct arch_flag {
const char *name;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
};
static const struct arch_flag arch_flags[] = {
{ "any", CPU_TYPE_ANY, CPU_SUBTYPE_MULTIPLE },
{ "little", CPU_TYPE_ANY, CPU_SUBTYPE_LITTLE_ENDIAN },
{ "big", CPU_TYPE_ANY, CPU_SUBTYPE_BIG_ENDIAN },
{ "ppc", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL },
{ "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
{ "m68k", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC680x0_ALL },
{ "hppa", CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_ALL },
{ "sparc", CPU_TYPE_SPARC, CPU_SUBTYPE_SPARC_ALL },
{ "m88k", CPU_TYPE_MC88000, CPU_SUBTYPE_MC88000_ALL },
{ "i860", CPU_TYPE_I860, CPU_SUBTYPE_I860_ALL },
{ "ppc601", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_601 },
{ "ppc603", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603 },
{ "ppc603e",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603e },
{ "ppc603ev",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603ev },
{ "ppc604", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604 },
{ "ppc604e",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604e },
{ "ppc750", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750 },
{ "ppc7400",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 },
{ "i486", CPU_TYPE_I386, CPU_SUBTYPE_486 },
{ "i486SX", CPU_TYPE_I386, CPU_SUBTYPE_486SX },
{ "pentium",CPU_TYPE_I386, CPU_SUBTYPE_PENT },
{ "i586", CPU_TYPE_I386, CPU_SUBTYPE_586 },
{ "pentpro", CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO },
{ "pentIIm3",CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M3 },
{ "pentIIm5",CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M5 },
{ "m68030", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68030_ONLY },
{ "m68040", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68040 },
{ "hppa7100LC", CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_7100LC },
{ NULL, 0, 0 }
};
static int
check_fatarch(
const struct fat_arch *arch,
cpu_type_t cputype,
cpu_subtype_t cpusubtype)
{
return (arch->cputype == cputype && arch->cpusubtype == cpusubtype);
}
static int
check_fatarchlist(
struct fat_arch *first_arch,
int nfat_archs,
int nsubtypes,
cpu_type_t cputype,
const cpu_subtype_t *cpusubtype)
{
struct fat_arch *arch;
int archi, j;
for (j = 0; j < nsubtypes; j++) {
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (check_fatarch(arch, cputype, cpusubtype[j]))
return archi;
}
}
return nfat_archs;
}
static
struct fat_arch *
cpusubtype_findbestarch(
cpu_type_t cputype,
cpu_subtype_t cpusubtype,
struct fat_header *fat,
off_t size)
{
unsigned long archi, lowest_family, lowest_model, lowest_index;
struct fat_arch *first_arch, *arch;
int nfat_archs;
nfat_archs = NXSwapBigLongToHost(fat->nfat_arch);
arch = (struct fat_arch *) (&fat[1]);
if ( (u_int8_t *) fat + size < (u_int8_t *) &arch[nfat_archs])
return NULL;
first_arch = malloc(nfat_archs * sizeof(struct fat_arch));
memcpy(first_arch, arch, nfat_archs * sizeof(struct fat_arch));
for (archi = 0, arch = first_arch; archi < nfat_archs; archi++, arch++) {
arch->cputype = NXSwapBigIntToHost(arch->cputype);
arch->cpusubtype = NXSwapBigIntToHost(arch->cpusubtype);
if (check_fatarch(arch, cputype, cpusubtype))
goto foundArch;
}
switch(cputype) {
case CPU_TYPE_I386:
switch(cpusubtype) {
default:
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (check_fatarch(arch, cputype, CPU_SUBTYPE_PENT))
goto foundArch;
}
case CPU_SUBTYPE_PENT:
case CPU_SUBTYPE_486SX:
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (check_fatarch(arch, cputype, CPU_SUBTYPE_486))
goto foundArch;
}
break;
case CPU_SUBTYPE_I386_ALL:
case CPU_SUBTYPE_486:
break;
}
{
static cpu_subtype_t sub_list[] = {
CPU_SUBTYPE_I386_ALL,
CPU_SUBTYPE_486,
CPU_SUBTYPE_486SX,
CPU_SUBTYPE_586,
};
archi = check_fatarchlist(first_arch,
nfat_archs,
sizeof(sub_list)/sizeof(sub_list[0]),
cputype,
sub_list);
if (archi != nfat_archs)
goto foundArch;
}
lowest_family = CPU_SUBTYPE_INTEL_FAMILY_MAX + 1;
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (arch->cputype == cputype
&& CPU_SUBTYPE_INTEL_FAMILY(arch->cpusubtype) < lowest_family) {
lowest_family = CPU_SUBTYPE_INTEL_FAMILY(arch->cpusubtype);
}
}
if (lowest_family == CPU_SUBTYPE_INTEL_FAMILY_MAX + 1) {
archi = nfat_archs;
goto foundArch;
}
lowest_model = ULONG_MAX;
lowest_index = -1;
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (arch->cputype == cputype
&& CPU_SUBTYPE_INTEL_FAMILY(arch->cpusubtype) == lowest_family
&& CPU_SUBTYPE_INTEL_MODEL( arch->cpusubtype) < lowest_model) {
lowest_model = CPU_SUBTYPE_INTEL_MODEL(arch->cpusubtype);
lowest_index = archi;
}
}
if (lowest_index == -1)
archi = nfat_archs;
else
archi = lowest_index;
goto foundArch;
case CPU_TYPE_MC680x0:
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (check_fatarch(arch, cputype, CPU_SUBTYPE_MC680x0_ALL))
goto foundArch;
}
archi = nfat_archs;
if (cpusubtype == CPU_SUBTYPE_MC680x0_ALL) {
static cpu_subtype_t sub_list[] = {
CPU_SUBTYPE_MC68040,
CPU_SUBTYPE_MC68030_ONLY,
};
archi = check_fatarchlist(first_arch,
nfat_archs,
sizeof(sub_list)/sizeof(sub_list[0]),
cputype,
sub_list);
}
goto foundArch;
case CPU_TYPE_POWERPC:
{
static cpu_subtype_t sub_list[] = {
CPU_SUBTYPE_POWERPC_7400,
CPU_SUBTYPE_POWERPC_750,
CPU_SUBTYPE_POWERPC_604e,
CPU_SUBTYPE_POWERPC_604,
CPU_SUBTYPE_POWERPC_603ev,
CPU_SUBTYPE_POWERPC_603e,
CPU_SUBTYPE_POWERPC_603,
CPU_SUBTYPE_POWERPC_ALL,
};
archi = check_fatarchlist(first_arch,
nfat_archs,
sizeof(sub_list)/sizeof(sub_list[0]),
cputype,
sub_list);
goto foundArch;
}
case CPU_TYPE_MC88000:
cpusubtype = CPU_SUBTYPE_MC88000_ALL; break;
case CPU_TYPE_I860:
cpusubtype = CPU_SUBTYPE_I860_ALL; break;
case CPU_TYPE_HPPA:
cpusubtype = CPU_SUBTYPE_HPPA_ALL; break;
case CPU_TYPE_SPARC:
cpusubtype = CPU_SUBTYPE_SPARC_ALL; break;
default:
goto foundArch;
}
arch = first_arch;
for (archi = 0; archi < nfat_archs; archi++, arch++) {
if (check_fatarch(arch, cputype, cpusubtype))
goto foundArch;
}
archi = nfat_archs;
foundArch:
free(first_arch);
arch = (struct fat_arch *) (&fat[1]);
return &arch[archi];
}
__private_extern__ void
find_arch(
u_int8_t **dataP,
off_t *sizeP,
cpu_type_t cputype,
cpu_subtype_t cpusubtype,
u_int8_t *data_ptr,
off_t filesize)
{
struct fat_header *fat_hdr;
struct mach_header *mach_hdr;
struct fat_arch *best_arch;
struct {
struct fat_header hdr;
struct fat_arch arch;
} fakeHeader;
int is_fat;
int is_mh;
fat_hdr = (struct fat_header *) data_ptr;
is_fat = (FAT_MAGIC == fat_hdr->magic || FAT_CIGAM == fat_hdr->magic);
mach_hdr = (struct mach_header *) data_ptr;
is_mh = (MH_MAGIC == mach_hdr->magic || MH_CIGAM == mach_hdr->magic);
if (cputype == CPU_TYPE_ANY || !(is_mh || is_fat)) {
if (sizeP) *sizeP = filesize;
if (dataP) *dataP = data_ptr;
return;
}
if (is_mh) {
fakeHeader.hdr.magic = FAT_MAGIC;
fakeHeader.hdr.nfat_arch = NXSwapHostLongToBig(1);
fakeHeader.arch.cputype = mach_hdr->cputype;
fakeHeader.arch.cpusubtype = mach_hdr->cpusubtype;
fakeHeader.arch.offset = 0;
fakeHeader.arch.size = NXSwapHostLongToBig((long) filesize);
fat_hdr = &fakeHeader.hdr;
}
best_arch = cpusubtype_findbestarch(cputype, cpusubtype, fat_hdr, filesize);
if (best_arch) {
if (sizeP) *sizeP = NXSwapBigLongToHost(best_arch->size);
if (dataP) *dataP = data_ptr + NXSwapBigLongToHost(best_arch->offset);
} else {
if (sizeP) *sizeP = 0;
if (dataP) *dataP = 0;
}
}
__private_extern__ int
get_arch_from_flag(char *name, cpu_type_t *cpuP, cpu_subtype_t *subcpuP)
{
const struct arch_flag *arch;
for (arch = arch_flags; arch->name != NULL; arch++) {
if (strcmp(arch->name, name) == 0) {
if (cpuP)
*cpuP = arch->cputype;
if (subcpuP)
*subcpuP = arch->cpusubtype;
return 1;
}
}
return 0;
}