#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ar.h>
#ifndef AR_EFMT1
#define AR_EFMT1 "#1/"
#endif
#include <limits.h>
#include <errno.h>
#include <ctype.h>
#include <libc.h>
#ifndef __OPENSTEP__
#include <time.h>
#include <utime.h>
#endif
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include "stuff/arch.h"
#include "stuff/errors.h"
#include "stuff/allocate.h"
#include "stuff/lto.h"
#include "stuff/write64.h"
#include "stuff/rnd.h"
#include "stuff/align.h"
#include "stuff/diagnostics.h"
#include <math.h>
#include <unistd.h>
#define MAXSECTALIGN 15
#undef TRUE
#undef FALSE
char *progname = NULL;
struct input_file {
char *name;
struct arch_flag arch_flag;
struct fat_header *fat_header;
struct fat_arch *fat_arches;
struct fat_arch_64 *fat_arches64;
enum bool is_thin;
uint32_t raw_nfat_arch;
};
static struct input_file *input_files = NULL;
static uint32_t ninput_files = 0;
struct thin_file {
char *name;
char *addr;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
uint64_t offset;
uint64_t size;
uint32_t align;
enum bool from_fat;
enum bool extract;
enum bool remove;
enum bool replace;
};
static struct thin_file *thin_files = NULL;
static uint32_t nthin_files = 0;
static char *output_file = NULL;
static uint32_t output_filemode = 0;
#ifndef __OPENSTEP__
static struct timespec output_times[2] = { 0 };
static struct timeval output_timev[2] = { 0 };
#else
static time_t output_timep[2] = { 0 };
#endif
static enum bool archives_in_input = FALSE;
static enum bool create_flag = FALSE;
static enum bool info_flag = FALSE;
static enum bool detailed_info_flag = FALSE;
static enum bool brief_info_flag = FALSE;
static enum bool thin_flag = FALSE;
static struct arch_flag thin_arch_flag = { 0 };
static enum bool remove_flag = FALSE;
static struct arch_flag *remove_arch_flags = NULL;
static uint32_t nremove_arch_flags = 0;
static enum bool extract_flag = FALSE;
static struct arch_flag *extract_arch_flags = NULL;
static uint32_t nextract_arch_flags = 0;
static enum bool extract_family_flag = FALSE;
static enum bool replace_flag = FALSE;
struct replace {
struct arch_flag arch_flag;
struct thin_file thin_file;
};
static struct replace *replaces = NULL;
static uint32_t nreplaces = 0;
struct segalign {
struct arch_flag arch_flag;
uint32_t align;
};
static struct segalign *segaligns = NULL;
static uint32_t nsegaligns = 0;
static struct fat_header fat_header = { 0 };
static enum bool verify_flag = FALSE;
static struct arch_flag *verify_archs = NULL;
static uint32_t nverify_archs = 0;
static enum bool fat64_flag = FALSE;
static enum bool hideARM64_flag = FALSE;
static void create_fat(
void);
static void process_input_file(
struct input_file *input);
static void process_replace_file(
struct replace *replace);
static void check_archive(
char *name,
char *addr,
uint64_t size,
cpu_type_t *cputype,
cpu_subtype_t *cpusubtype);
static void check_extend_format_1(
char *name,
struct ar_hdr *ar_hdr,
uint64_t size_left,
uint32_t *member_name_size);
static uint32_t get_mh_filetype(
char* addr,
uint64_t size);
static void print_arch(
cpu_type_t cputype,
cpu_subtype_t cpusubtype);
static void print_cputype(
cpu_type_t cputype,
cpu_subtype_t cpusubtype);
static int size_ar_name(
char *ar_name);
static struct input_file *new_input(
void);
static struct thin_file *new_thin(
void);
static struct arch_flag *new_arch_flag(
struct arch_flag **arch_flags,
uint32_t *narch_flags);
static struct replace *new_replace(
void);
static struct segalign *new_segalign(
void);
static int cmp_qsort(
const struct thin_file *thin1,
const struct thin_file *thin2);
static enum bool ispoweroftwo(
uint32_t x);
static void check_arch(
struct input_file *input,
struct thin_file *thin);
static void usage(
void);
extern char apple_version[];
char *version = apple_version;
int
main(
int argc,
char *argv[],
char *envp[])
{
int fd, a;
uint32_t i, j, k, value;
char *p, *endp;
struct input_file *input;
struct arch_flag *arch_flag;
struct replace *replace;
struct segalign *segalign;
const struct arch_flag *arch_flags;
enum bool found;
int time_result;
input = NULL;
diagnostics_enable(getenv("CC_LOG_DIAGNOSTICS") != NULL);
diagnostics_output(getenv("CC_LOG_DIAGNOSTICS_FILE"));
diagnostics_log_args(argc, argv);
progname = argv[0];
for(a = 1; a < argc; a++){
if(argv[a][0] == '-'){
p = &(argv[a][1]);
switch(*p){
case 'a':
if(strcmp(p, "arch") == 0 || strcmp(p, "a") == 0){
if(a + 2 >= argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
input = new_input();
if(get_arch_from_flag(argv[a+1],
&(input->arch_flag)) == 0){
error("unknown architecture specification flag: %s "
"in specifying input file %s %s %s", argv[a+1],
argv[a], argv[a+1], argv[a+2]);
arch_usage();
usage();
}
input->name = argv[a+2];
a += 2;
}
else if(strcmp(p, "arch_blank") == 0){
fatal("flag %s is no longer supported.", argv[a]);
}
else if(strcmp(p, "archs") == 0){
brief_info_flag = TRUE;
}
else
goto unknown_flag;
break;
case 'c':
if(strcmp(p, "create") == 0 || strcmp(p, "c") == 0){
create_flag = TRUE;
}
else
goto unknown_flag;
break;
case 'd':
if(strcmp(p, "detailed_info") == 0 || strcmp(p, "d") == 0){
detailed_info_flag = TRUE;
}
else
goto unknown_flag;
break;
case 'e':
if(strcmp(p, "extract") == 0 ||
strcmp(p, "extract_family") == 0 ||
strcmp(p, "e") == 0){
extract_flag = TRUE;
if(strcmp(p, "extract_family") == 0)
extract_family_flag = TRUE;
if(a + 1 >= argc){
error("missing argument to %s option", argv[a]);
usage();
}
arch_flag = new_arch_flag(&extract_arch_flags,
&nextract_arch_flags);
if(get_arch_from_flag(argv[a+1], arch_flag) == 0){
error("unknown architecture specification flag: "
"%s in specifying extract operation: %s %s",
argv[a+1], argv[a], argv[a+1]);
arch_usage();
usage();
}
a++;
}
else
goto unknown_flag;
break;
case 'h':
if (strcmp(p, "hideARM64") == 0) {
hideARM64_flag = TRUE;
}
else
goto unknown_flag;
break;
case 'i':
if(strcmp(p, "info") == 0 || strcmp(p, "i") == 0){
info_flag = TRUE;
}
else
goto unknown_flag;
break;
case 'o':
if(strcmp(p, "output") == 0 || strcmp(p, "o") == 0){
if(a + 1 >= argc){
error("missing argument to %s option", argv[a]);
usage();
}
if(output_file != NULL)
fatal("more than one %s option specified", argv[a]);
output_file = argv[a + 1];
a++;
}
else
goto unknown_flag;
break;
case 'r':
if(strcmp(p, "remove") == 0 || strcmp(p, "rem") == 0){
remove_flag = TRUE;
if(a + 1 >= argc){
error("missing argument to %s option", argv[a]);
usage();
}
arch_flag = new_arch_flag(&remove_arch_flags,
&nremove_arch_flags);
if(get_arch_from_flag(argv[a+1], arch_flag) == 0){
error("unknown architecture specification flag: "
"%s in specifying remove operation: %s %s",
argv[a+1], argv[a], argv[a+1]);
arch_usage();
usage();
}
a++;
}
else if(strcmp(p, "replace") == 0 || strcmp(p, "rep") == 0){
replace_flag = TRUE;
if(a + 2 >= argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
replace = new_replace();
if(get_arch_from_flag(argv[a+1],
&(replace->arch_flag)) == 0){
error("unknown architecture specification flag: "
"%s in specifying replace operation: %s %s %s",
argv[a+1], argv[a], argv[a+1], argv[a+2]);
arch_usage();
usage();
}
replace->thin_file.name = argv[a+2];
a += 2;
}
else
goto unknown_flag;
break;
case 's':
if(strcmp(p, "segalign") == 0 || strcmp(p, "s") == 0){
if(a + 2 >= argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
segalign = new_segalign();
if(get_arch_from_flag(argv[a+1],
&(segalign->arch_flag)) == 0){
error("unknown architecture specification flag: "
"%s in specifying segment alignment: %s %s %s",
argv[a+1], argv[a], argv[a+1], argv[a+2]);
arch_usage();
usage();
}
value = (uint32_t)strtoul(argv[a+2], &endp, 16);
if(*endp != '\0')
fatal("argument for -segalign <arch_type> %s not a "
"proper hexadecimal number", argv[a+2]);
if(!ispoweroftwo(value) || value == 0)
fatal("argument to -segalign <arch_type> %x (hex) "
"must be a non-zero power of two", value);
if(value > (1 << MAXSECTALIGN))
fatal("argument to -segalign <arch_type> %x (hex) "
"must equal to or less than %x (hex)",
value, (unsigned int)(1 << MAXSECTALIGN));
segalign->align = 0;
while((value & 0x1) != 1){
value >>= 1;
segalign->align++;
}
a += 2;
}
else
goto unknown_flag;
break;
case 't':
if(strcmp(p, "thin") == 0 || strcmp(p, "t") == 0){
if(thin_flag == TRUE)
fatal("more than one %s option specified", argv[a]);
thin_flag = TRUE;
if(a + 1 >= argc){
error("missing argument to %s option", argv[a]);
usage();
}
if(get_arch_from_flag(argv[a+1], &thin_arch_flag) == 0){
error("unknown architecture specification flag: "
"%s in specifying thin operation: %s %s",
argv[a+1], argv[a], argv[a+1]);
arch_usage();
usage();
}
a++;
}
else
goto unknown_flag;
break;
case 'v':
if(strcmp(p, "verify_arch") == 0){
verify_flag = TRUE;
if(a + 1 >= argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
a++;
nverify_archs = argc - a;
verify_archs = (struct arch_flag *)allocate(
sizeof(struct arch_flag) * nverify_archs);
for(i = 0; a < argc; a++, i++){
if(get_arch_from_flag(argv[a],
verify_archs + i) == 0){
error("unknown architecture specification "
"flag: %s in specifying -verify_arch "
"operation", argv[a]);
arch_usage();
usage();
}
}
}
else
goto unknown_flag;
break;
case 'f':
if(strcmp(p, "fat64") == 0)
fat64_flag = TRUE;
else
goto unknown_flag;
break;
default:
unknown_flag:
fatal("unknown flag: %s", argv[a]);
}
}
else{
input = new_input();
input->name = argv[a];
}
}
if(info_flag == FALSE && detailed_info_flag == FALSE &&
brief_info_flag == FALSE &&
create_flag == FALSE && thin_flag == FALSE &&
extract_flag == FALSE && remove_flag == FALSE &&
replace_flag == FALSE && verify_flag == FALSE){
error("one of -create, -thin <arch_type>, -extract <arch_type>, "
"-remove <arch_type>, -replace <arch_type> <file_name>, "
"-verify_arch <arch_type> ... , "
"-archs, -info, or -detailed_info must be specified");
usage();
}
if((create_flag == TRUE || thin_flag == TRUE || extract_flag == TRUE ||
remove_flag == TRUE || replace_flag == TRUE) &&
output_file == NULL){
error("no output file specified");
usage();
}
if(ninput_files == 0){
error("no input files specified");
usage();
}
if(verify_flag == TRUE && ninput_files != 1){
error("only one input file allowed with -verify_arch");
usage();
}
if(brief_info_flag == TRUE && ninput_files != 1){
error("only one input file allowed with -archs");
usage();
}
if(create_flag + thin_flag + extract_flag + remove_flag + replace_flag +
info_flag + detailed_info_flag + brief_info_flag + verify_flag > 1){
error("only one of -create, -thin <arch_type>, -extract <arch_type>"
", -remove <arch_type>, -replace <arch_type> <file_name>, "
"-verify_arch <arch_type> ..., "
"-info, -archs, or -detailed_info can be specified");
usage();
}
if (hideARM64_flag == TRUE && create_flag == FALSE &&
replace_flag == FALSE && remove_flag == FALSE) {
error("-hideARM64 may only be used with -create, -remove, or "
"-replace");
usage();
}
for(i = 0; i < ninput_files; i++)
process_input_file(input_files + i);
if(create_flag){
for(i = 0; i < nthin_files; i++)
for(j = i + 1; j < nthin_files; j++)
if(thin_files[i].cputype ==
thin_files[j].cputype &&
(thin_files[i].cpusubtype & ~CPU_SUBTYPE_MASK)==
(thin_files[j].cpusubtype & ~CPU_SUBTYPE_MASK)){
arch_flags = get_arch_flags();
for(k = 0; arch_flags[k].name != NULL; k++){
if(arch_flags[k].cputype ==
thin_files[j].cputype &&
(arch_flags[k].cpusubtype &
~CPU_SUBTYPE_MASK) ==
(thin_files[j].cpusubtype &
~CPU_SUBTYPE_MASK))
fatal("%s and %s have the same architectures (%s) "
"and can't be in the same fat output file",
thin_files[i].name, thin_files[j].name,
arch_flags[k].name);
}
fatal("%s and %s have the same architectures (cputype "
"(%d) and cpusubtype (%d)) and can't be in the "
"same fat output file", thin_files[i].name,
thin_files[j].name,thin_files[i].cputype,
thin_files[i].cpusubtype &
~CPU_SUBTYPE_MASK);
}
create_fat();
}
if(thin_flag){
if(ninput_files != 1)
fatal("only one input file can be specified with the -thin "
"option");
if(input_files[0].fat_header == NULL)
fatal("input file (%s) must be a fat file when the -thin "
"option is specified", input_files[0].name);
for(i = 0; i < nthin_files; i++){
if(thin_files[i].cputype == thin_arch_flag.cputype &&
(thin_files[i].cpusubtype & ~CPU_SUBTYPE_MASK) ==
(thin_arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK)){
(void)unlink(output_file);
if((fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC,
output_filemode)) == -1)
system_fatal("can't create output file: %s",
output_file);
if (write64(fd, thin_files[i].addr, thin_files[i].size) !=
thin_files[i].size)
system_fatal("can't write thin file to output "
"file: %s", output_file);
if(close(fd) == -1)
system_fatal("can't close output file: %s",output_file);
#ifndef __OPENSTEP__
if (__builtin_available(macOS 10.12, *)) {
time_result = utimensat(AT_FDCWD, output_file,
output_times, 0);
}
else {
time_result = utimes(output_file, output_timev);
}
#else
time_result = utime(output_file, output_timep);
#endif
if (time_result == -1)
system_fatal("can't set the modify times for "
"output file: %s", output_file);
break;
}
}
if(i == nthin_files)
fatal("fat input file (%s) does not contain the specified "
"architecture (%s) to thin it to", input->name,
thin_arch_flag.name);
}
if(extract_flag){
if(ninput_files != 1)
fatal("only one input file can be specified with the -extract "
"option");
if(input_files[0].fat_header == NULL)
fatal("input file (%s) must be a fat file when the -extract "
"option is specified", input_files[0].name);
if(input_files[0].fat_header->magic == FAT_MAGIC_64)
fat64_flag = TRUE;
for(i = 0; i < nextract_arch_flags; i++){
for(j = i + 1; j < nextract_arch_flags; j++){
if(extract_arch_flags[i].cputype ==
extract_arch_flags[j].cputype &&
(extract_arch_flags[i].cpusubtype & ~CPU_SUBTYPE_MASK) ==
(extract_arch_flags[j].cpusubtype & ~CPU_SUBTYPE_MASK))
fatal("-extract %s specified multiple times",
extract_arch_flags[i].name);
}
}
for(i = 0; i < nextract_arch_flags; i++){
found = FALSE;
for(j = 0; j < nthin_files; j++){
if(extract_arch_flags[i].cputype ==
thin_files[j].cputype &&
((extract_arch_flags[i].cpusubtype &
~CPU_SUBTYPE_MASK)==
(thin_files[j].cpusubtype &
~CPU_SUBTYPE_MASK) ||
extract_family_flag == TRUE)){
thin_files[j].extract = TRUE;
found = TRUE;
}
}
if(found == FALSE)
fatal("-extract %s specified but fat file: %s does not "
"contain that architecture",
extract_arch_flags[i].name, input_files[0].name);
}
for(i = 0; i < nthin_files; ){
if(thin_files[i].extract == FALSE){
for(j = i; j < nthin_files - 1; j++)
thin_files[j] = thin_files[j + 1];
nthin_files--;
}
else
i++;
}
create_fat();
}
if(remove_flag){
if(ninput_files != 1)
fatal("only one input file can be specified with the -remove "
"option");
if(input_files[0].fat_header == NULL)
fatal("input file (%s) must be a fat file when the -remove "
"option is specified", input_files[0].name);
if(input_files[0].fat_header->magic == FAT_MAGIC_64)
fat64_flag = TRUE;
for(i = 0; i < nremove_arch_flags; i++){
for(j = i + 1; j < nremove_arch_flags; j++){
if(remove_arch_flags[i].cputype ==
remove_arch_flags[j].cputype &&
(remove_arch_flags[i].cpusubtype & ~CPU_SUBTYPE_MASK) ==
(remove_arch_flags[j].cpusubtype & ~CPU_SUBTYPE_MASK))
fatal("-remove %s specified multiple times",
remove_arch_flags[i].name);
}
}
for(i = 0; i < nremove_arch_flags; i++){
for(j = 0; j < nthin_files; j++){
if(remove_arch_flags[i].cputype ==
thin_files[j].cputype &&
(remove_arch_flags[i].cpusubtype &
~CPU_SUBTYPE_MASK) ==
(thin_files[j].cpusubtype &
~CPU_SUBTYPE_MASK)){
thin_files[j].remove = TRUE;
break;
}
}
if(j == nthin_files)
fatal("-remove %s specified but fat file: %s does not "
"contain that architecture",
remove_arch_flags[i].name, input_files[0].name);
}
for(i = 0; i < nthin_files; ){
if(thin_files[i].remove == TRUE){
for(j = i; j < nthin_files; j++)
thin_files[j] = thin_files[j + 1];
nthin_files--;
}
else
i++;
}
if(nthin_files == 0)
fatal("-remove's specified would result in an empty fat file");
create_fat();
}
if(replace_flag){
if(ninput_files != 1)
fatal("only one input file can be specified with the -replace "
"option");
if(input_files[0].fat_header == NULL)
fatal("input file (%s) must be a fat file when the -replace "
"option is specified", input_files[0].name);
if(input_files[0].fat_header->magic == FAT_MAGIC_64)
fat64_flag = TRUE;
for(i = 0; i < nreplaces; i++){
for(j = i + 1; j < nreplaces; j++){
if(replaces[i].arch_flag.cputype ==
replaces[j].arch_flag.cputype &&
(replaces[i].arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK) ==
(replaces[j].arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK))
fatal("-replace %s <file_name> specified multiple times",
replaces[j].arch_flag.name);
}
}
for(i = 0; i < nreplaces; i++){
process_replace_file(replaces + i);
for(j = 0; j < nthin_files; j++){
if(replaces[i].arch_flag.cputype ==
thin_files[j].cputype &&
(replaces[i].arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK) ==
(thin_files[j].cpusubtype & ~CPU_SUBTYPE_MASK)){
thin_files[j] = replaces[i].thin_file;
break;
}
}
if(j == nthin_files)
fatal("-replace %s <file_name> specified but fat file: %s "
"does not contain that architecture",
replaces[i].arch_flag.name, input_files[0].name);
}
create_fat();
}
if (brief_info_flag) {
for (i = 0; i < nthin_files; i++) {
const char* s = get_arch_name_if_known(
thin_files[i].cputype,
thin_files[i].cpusubtype);
if (i) {
printf(" ");
}
if (s) {
printf("%s", s);
}
else {
printf("unknown(%u,%u)", thin_files[i].cputype,
thin_files[i].cpusubtype & ~CPU_SUBTYPE_MASK);
}
}
printf("\n");
}
if(info_flag){
for(i = 0; i < ninput_files; i++){
if(input_files[i].fat_header != NULL){
printf("Architectures in the fat file: %s are: ",
input_files[i].name);
if(input_files[i].fat_arches != NULL){
for(j = 0; j < input_files[i].fat_header->nfat_arch;
j++){
print_arch(input_files[i].fat_arches[j].cputype,
input_files[i].fat_arches[j].cpusubtype);
printf(" ");
}
}
else{
for(j = 0; j < input_files[i].fat_header->nfat_arch;
j++){
print_arch(input_files[i].fat_arches64[j].cputype,
input_files[i].fat_arches64[j].cpusubtype);
printf(" ");
}
}
printf("\n");
}
else{
if(input_files[i].is_thin == FALSE)
printf("input file %s is not a fat file\n",
input_files[i].name);
}
}
for(i = 0; i < nthin_files; i++){
if(thin_files[i].from_fat == TRUE)
continue;
printf("Non-fat file: %s is architecture: %s\n",
thin_files[i].name,
get_arch_name_from_types(thin_files[i].cputype,
thin_files[i].cpusubtype & ~CPU_SUBTYPE_MASK));
}
}
if(detailed_info_flag){
for(i = 0; i < ninput_files; i++){
if(input_files[i].fat_header != NULL){
printf("Fat header in: %s\n", input_files[i].name);
printf("fat_magic 0x%x\n",
(unsigned int)(input_files[i].fat_header->magic));
printf("nfat_arch %u",
input_files[i].raw_nfat_arch);
if (input_files[i].fat_header->nfat_arch -
input_files[i].raw_nfat_arch) {
printf(" (+%u hidden)",
input_files[i].fat_header->nfat_arch -
input_files[i].raw_nfat_arch);
}
printf("\n");
for(j = 0; j < input_files[i].fat_header->nfat_arch; j++){
printf("architecture ");
if(input_files[i].fat_arches != NULL){
print_arch(input_files[i].fat_arches[j].cputype,
input_files[i].fat_arches[j].cpusubtype);
if (j >= input_files[i].raw_nfat_arch) {
printf(" (hidden)");
}
printf("\n");
print_cputype(input_files[i].fat_arches[j].cputype,
input_files[i].fat_arches[j].cpusubtype);
printf(" offset %u\n",
input_files[i].fat_arches[j].offset);
printf(" size %u\n",
input_files[i].fat_arches[j].size);
printf(" align 2^%u (%d)\n",
input_files[i].fat_arches[j].align,
1 << input_files[i].fat_arches[j].align);
}
else{
print_arch(input_files[i].fat_arches64[j].cputype,
input_files[i].fat_arches64[j].cpusubtype);
if (j >= input_files[i].raw_nfat_arch) {
printf(" (hidden)");
}
printf("\n");
print_cputype(
input_files[i].fat_arches64[j].cputype,
input_files[i].fat_arches64[j].cpusubtype);
printf(" offset %llu\n",
input_files[i].fat_arches64[j].offset);
printf(" size %llu\n",
input_files[i].fat_arches64[j].size);
printf(" align 2^%u (%d)\n",
input_files[i].fat_arches64[j].align,
1 << input_files[i].fat_arches64[j].align);
}
}
}
else{
printf("input file %s is not a fat file\n",
input_files[i].name);
}
}
for(i = 0; i < nthin_files; i++){
if(thin_files[i].from_fat == TRUE)
continue;
printf("Non-fat file: %s is architecture: %s\n",
thin_files[i].name,
get_arch_name_from_types(thin_files[i].cputype,
thin_files[i].cpusubtype & ~CPU_SUBTYPE_MASK));
}
}
if(verify_flag == TRUE){
for(i = 0; i < nverify_archs; i++){
found = FALSE;
for(j = 0; j < nthin_files; j++){
if(verify_archs[i].cputype ==
thin_files[j].cputype &&
(verify_archs[i].cpusubtype & ~CPU_SUBTYPE_MASK) ==
(thin_files[j].cpusubtype & ~CPU_SUBTYPE_MASK)){
found = TRUE;
break;
}
}
if(found == FALSE){
exit(1);
}
}
}
return (errors ? 1 : 0);
}
static
void
create_fat(void)
{
uint32_t i, j;
uint64_t offset;
char *rename_file;
int fd;
struct fat_arch fat_arch;
struct fat_arch_64 fat_arch64;
for(i = 0; i < nsegaligns; i++){
for(j = i + 1; j < nsegaligns; j++){
if(segaligns[i].arch_flag.cputype ==
segaligns[j].arch_flag.cputype &&
(segaligns[i].arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK) ==
(segaligns[j].arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK))
fatal("-segalign %s <value> specified multiple times",
segaligns[j].arch_flag.name);
}
}
for(i = 0; i < nsegaligns; i++){
for(j = 0; j < nthin_files; j++){
if(segaligns[i].arch_flag.cputype ==
thin_files[j].cputype &&
(segaligns[i].arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK) ==
(thin_files[j].cpusubtype & ~CPU_SUBTYPE_MASK)){
thin_files[j].align = segaligns[i].align;
break;
}
}
if(j == nthin_files)
fatal("-segalign %s <value> specified but resulting fat "
"file does not contain that architecture",
segaligns[i].arch_flag.name);
}
qsort(thin_files, nthin_files, sizeof(struct thin_file),
(int (*)(const void *, const void *))cmp_qsort);
if(fat64_flag == TRUE)
fat_header.magic = FAT_MAGIC_64;
else
fat_header.magic = FAT_MAGIC;
fat_header.nfat_arch = nthin_files;
if (hideARM64_flag) {
enum bool has_arm32 = FALSE;
enum bool has_arm64 = FALSE;
uint32_t num_archs = 0;
uint32_t num_archs_arm64 = 0;
for(i = 0; i < nthin_files; i++){
if (MH_EXECUTE != get_mh_filetype(thin_files[i].addr,
thin_files[i].size)) {
fatal("-hideARM64 specified but thin file %s is not of "
"type MH_EXECUTE", thin_files[i].name);
}
if (has_arm64 && thin_files[i].cputype != CPU_TYPE_ARM64) {
fatal("-hideARM64 specified but thin files are not in "
"correct order");
}
if (thin_files[i].cputype == CPU_TYPE_ARM) {
has_arm32 = TRUE;
}
if (thin_files[i].cputype == CPU_TYPE_ARM64) {
has_arm64 = TRUE;
num_archs_arm64 += 1;
}
num_archs += 1;
}
if (has_arm32 && has_arm64) {
fat_header.nfat_arch = num_archs - num_archs_arm64;
}
}
offset = sizeof(struct fat_header);
if(fat64_flag == TRUE)
offset += nthin_files * sizeof(struct fat_arch_64);
else
offset += nthin_files * sizeof(struct fat_arch);
for(i = 0; i < nthin_files; i++){
offset = rnd(offset, 1 << thin_files[i].align);
if(fat64_flag == FALSE && offset > UINT32_MAX)
fatal("fat file too large to be created because the offset "
"field in struct fat_arch is only 32-bits and the offset "
"(%llu) for %s for architecture %s exceeds that",
offset, thin_files[i].name,
get_arch_name_from_types(thin_files[i].cputype,
thin_files[i].cpusubtype & ~CPU_SUBTYPE_MASK));
thin_files[i].offset = offset;
offset += thin_files[i].size;
}
rename_file = makestr(output_file, ".lipo", NULL);
if((fd = open(rename_file, O_WRONLY | O_CREAT | O_TRUNC,
output_filemode)) == -1)
system_fatal("can't create temporary output file: %s", rename_file);
if(extract_family_flag != TRUE || nthin_files != 1){
#ifdef __LITTLE_ENDIAN__
swap_fat_header(&fat_header, BIG_ENDIAN_BYTE_SEX);
#endif
if(write64(fd, &fat_header, sizeof(struct fat_header)) !=
sizeof(struct fat_header))
system_fatal("can't write fat header to output file: %s",
rename_file);
#ifdef __LITTLE_ENDIAN__
swap_fat_header(&fat_header, LITTLE_ENDIAN_BYTE_SEX);
#endif
for(i = 0; i < nthin_files; i++){
if(fat64_flag == TRUE){
fat_arch64.cputype = thin_files[i].cputype;
fat_arch64.cpusubtype = thin_files[i].cpusubtype;
fat_arch64.offset = thin_files[i].offset;
fat_arch64.size = thin_files[i].size;
fat_arch64.align = thin_files[i].align;
}
else{
fat_arch.cputype = thin_files[i].cputype;
fat_arch.cpusubtype = thin_files[i].cpusubtype;
fat_arch.offset = (uint32_t)thin_files[i].offset;
fat_arch.size = (uint32_t)thin_files[i].size;
fat_arch.align = thin_files[i].align;
}
if(fat64_flag == TRUE){
#ifdef __LITTLE_ENDIAN__
swap_fat_arch_64(&fat_arch64, 1, BIG_ENDIAN_BYTE_SEX);
#endif
if(write64(fd, &fat_arch64, sizeof(struct fat_arch_64)) !=
sizeof(struct fat_arch_64))
system_fatal("can't write fat arch to output file: %s",
rename_file);
}
else{
#ifdef __LITTLE_ENDIAN__
swap_fat_arch(&fat_arch, 1, BIG_ENDIAN_BYTE_SEX);
#endif
if(write64(fd, &fat_arch, sizeof(struct fat_arch)) !=
sizeof(struct fat_arch))
system_fatal("can't write fat arch to output file: %s",
rename_file);
}
}
}
for(i = 0; i < nthin_files; i++){
if(extract_family_flag == FALSE || nthin_files > 1)
if(lseek(fd, thin_files[i].offset, L_SET) == -1)
system_fatal("can't lseek in output file: %s", rename_file);
if(write64(fd, thin_files[i].addr, thin_files[i].size) !=
thin_files[i].size)
system_fatal("can't write to output file: %s", rename_file);
}
if(close(fd) == -1)
system_fatal("can't close output file: %s", rename_file);
if(rename(rename_file, output_file) == -1)
system_error("can't move temporary file: %s to file: %s",
output_file, rename_file);
free(rename_file);
}
static
void
process_input_file(
struct input_file *input)
{
int fd;
struct stat stat_buf, stat_buf2;
uint32_t i, j;
uint64_t size;
char *addr;
struct thin_file *thin;
struct mach_header *mhp, mh;
struct mach_header_64 *mhp64, mh64;
struct load_command *lcp;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
enum bool swapped;
uint64_t big_size;
uint32_t offset, first_offset;
if((fd = open(input->name, O_RDONLY)) == -1)
system_fatal("can't open input file: %s", input->name);
if(fstat(fd, &stat_buf) == -1)
system_fatal("can't stat input file: %s", input->name);
size = stat_buf.st_size;
output_filemode = stat_buf.st_mode & 07777;
#ifndef __OPENSTEP__
if (__builtin_available(macOS 10.12, *)) {
if (output_times[1].tv_sec == 0) {
memcpy(&output_times[0], &stat_buf.st_atimespec,
sizeof(struct timespec));
memcpy(&output_times[1], &stat_buf.st_mtimespec,
sizeof(struct timespec));
}
} else {
if (output_timev[1].tv_sec == 0) {
TIMESPEC_TO_TIMEVAL(&output_timev[0], &stat_buf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&output_timev[1], &stat_buf.st_mtimespec);
}
}
#else
if(output_timep[1] == 0 || output_timep[1] > stat_buf.st_mtime){
output_timep[0] = stat_buf.st_atime;
output_timep[1] = stat_buf.st_mtime;
}
#endif
if((stat_buf.st_mode & S_IFREG) == S_IFREG && size == 0)
addr = NULL;
else
addr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE,
fd, 0);
if((intptr_t)addr == -1)
system_fatal("can't map input file: %s", input->name);
if(fstat(fd, &stat_buf2) == -1)
system_fatal("can't stat input file: %s", input->name);
if(stat_buf2.st_size != size ||
stat_buf2.st_mtime != stat_buf.st_mtime)
system_fatal("Input file: %s changed since opened", input->name);
close(fd);
if(size >= sizeof(struct fat_header) &&
#ifdef __BIG_ENDIAN__
*((uint32_t *)addr) == FAT_MAGIC)
#endif
#ifdef __LITTLE_ENDIAN__
*((uint32_t *)addr) == SWAP_INT(FAT_MAGIC))
#endif
{
if(input->arch_flag.name != NULL)
fatal("architecture specifed for fat input file: %s "
"(architectures can't be specifed for fat input files)",
input->name);
input->fat_header = (struct fat_header *)addr;
#ifdef __LITTLE_ENDIAN__
swap_fat_header(input->fat_header, LITTLE_ENDIAN_BYTE_SEX);
#endif
big_size = input->fat_header->nfat_arch;
big_size *= sizeof(struct fat_arch);
big_size += sizeof(struct fat_header);
if(big_size > size)
fatal("truncated or malformed fat file (fat_arch structs would "
"extend past the end of the file) %s", input->name);
input->fat_arches = (struct fat_arch *)
(addr + sizeof(struct fat_header));
first_offset = 0xFFFFFFFF;
input->raw_nfat_arch = input->fat_header->nfat_arch;
for(i = 0; i < input->fat_header->nfat_arch; i++){
offset = input->fat_arches[i].offset;
#ifdef __LITTLE_ENDIAN__
cputype = OSSwapInt32(offset);
#endif
if (offset < first_offset)
first_offset = offset;
}
if (big_size + sizeof(struct fat_arch) <= size &&
big_size + sizeof(struct fat_arch) <= first_offset) {
i = input->fat_header->nfat_arch;
cputype = input->fat_arches[i].cputype;
#ifdef __LITTLE_ENDIAN__
cputype = OSSwapInt32(cputype);
#endif
if (cputype == CPU_TYPE_ARM64)
input->fat_header->nfat_arch += 1;
}
#ifdef __LITTLE_ENDIAN__
swap_fat_arch(input->fat_arches, input->fat_header->nfat_arch,
LITTLE_ENDIAN_BYTE_SEX);
#endif
for(i = 0; i < input->fat_header->nfat_arch; i++){
if(input->fat_arches[i].offset + input->fat_arches[i].size >
size)
fatal("truncated or malformed fat file (offset plus size "
"of cputype (%d) cpusubtype (%d) extends past the "
"end of the file) %s", input->fat_arches[i].cputype,
input->fat_arches[i].cpusubtype & ~CPU_SUBTYPE_MASK,
input->name);
if(input->fat_arches[i].align > MAXSECTALIGN)
fatal("align (2^%u) too large of fat file %s (cputype (%d)"
" cpusubtype (%d)) (maximum 2^%d)",
input->fat_arches[i].align, input->name,
input->fat_arches[i].cputype,
input->fat_arches[i].cpusubtype & ~CPU_SUBTYPE_MASK,
MAXSECTALIGN);
if(input->fat_arches[i].offset %
(1 << input->fat_arches[i].align) != 0)
fatal("offset %u of fat file %s (cputype (%d) cpusubtype "
"(%d)) not aligned on its alignment (2^%u)",
input->fat_arches[i].offset, input->name,
input->fat_arches[i].cputype,
input->fat_arches[i].cpusubtype & ~CPU_SUBTYPE_MASK,
input->fat_arches[i].align);
}
for(i = 0; i < input->fat_header->nfat_arch; i++){
for(j = i + 1; j < input->fat_header->nfat_arch; j++){
if(input->fat_arches[i].cputype ==
input->fat_arches[j].cputype &&
(input->fat_arches[i].cpusubtype & ~CPU_SUBTYPE_MASK) ==
(input->fat_arches[j].cpusubtype & ~CPU_SUBTYPE_MASK))
fatal("fat file %s contains two of the same architecture "
"(cputype (%d) cpusubtype (%d))", input->name,
input->fat_arches[i].cputype,
input->fat_arches[i].cpusubtype & ~CPU_SUBTYPE_MASK);
}
}
for(i = 0; i < input->fat_header->nfat_arch; i++){
thin = new_thin();
thin->name = input->name;
thin->addr = addr + input->fat_arches[i].offset;
thin->cputype = input->fat_arches[i].cputype;
thin->cpusubtype = input->fat_arches[i].cpusubtype;
thin->offset = input->fat_arches[i].offset;
thin->size = input->fat_arches[i].size;
thin->align = input->fat_arches[i].align;
thin->from_fat = TRUE;
if(input->fat_arches[i].size >= SARMAG &&
strncmp(thin->addr, ARMAG, SARMAG) == 0)
archives_in_input = TRUE;
}
}
else if(size >= sizeof(struct fat_header) &&
#ifdef __BIG_ENDIAN__
*((uint32_t *)addr) == FAT_MAGIC_64)
#endif
#ifdef __LITTLE_ENDIAN__
*((uint32_t *)addr) == SWAP_INT(FAT_MAGIC_64))
#endif
{
if(input->arch_flag.name != NULL)
fatal("architecture specifed for fat input file: %s "
"(architectures can't be specifed for fat input files)",
input->name);
input->fat_header = (struct fat_header *)addr;
#ifdef __LITTLE_ENDIAN__
swap_fat_header(input->fat_header, LITTLE_ENDIAN_BYTE_SEX);
#endif
big_size = input->fat_header->nfat_arch;
big_size *= sizeof(struct fat_arch_64);
big_size += sizeof(struct fat_header);
if(big_size > size)
fatal("truncated or malformed fat file (fat_arch_64 structs "
"would extend past the end of the file) %s", input->name);
input->fat_arches64 = (struct fat_arch_64 *)
(addr + sizeof(struct fat_header));
#ifdef __LITTLE_ENDIAN__
swap_fat_arch_64(input->fat_arches64, input->fat_header->nfat_arch,
LITTLE_ENDIAN_BYTE_SEX);
#endif
for(i = 0; i < input->fat_header->nfat_arch; i++){
if(input->fat_arches64[i].offset + input->fat_arches64[i].size >
size)
fatal("truncated or malformed fat file (offset plus size "
"of cputype (%d) cpusubtype (%d) extends past the "
"end of the file) %s", input->fat_arches64[i].cputype,
input->fat_arches64[i].cpusubtype & ~CPU_SUBTYPE_MASK,
input->name);
if(input->fat_arches64[i].align > MAXSECTALIGN)
fatal("align (2^%u) too large of fat file %s (cputype (%d)"
" cpusubtype (%d)) (maximum 2^%d)",
input->fat_arches64[i].align, input->name,
input->fat_arches64[i].cputype,
input->fat_arches64[i].cpusubtype & ~CPU_SUBTYPE_MASK,
MAXSECTALIGN);
if(input->fat_arches64[i].offset %
(1 << input->fat_arches64[i].align) != 0)
fatal("offset %llu of fat file %s (cputype (%d) cpusubtype "
"(%d)) not aligned on its alignment (2^%u)",
input->fat_arches64[i].offset, input->name,
input->fat_arches64[i].cputype,
input->fat_arches64[i].cpusubtype & ~CPU_SUBTYPE_MASK,
input->fat_arches64[i].align);
}
for(i = 0; i < input->fat_header->nfat_arch; i++){
for(j = i + 1; j < input->fat_header->nfat_arch; j++){
if(input->fat_arches64[i].cputype ==
input->fat_arches64[j].cputype &&
(input->fat_arches64[i].cpusubtype &
~CPU_SUBTYPE_MASK) ==
(input->fat_arches64[j].cpusubtype &
~CPU_SUBTYPE_MASK))
fatal("fat file %s contains two of the same architecture "
"(cputype (%d) cpusubtype (%d))", input->name,
input->fat_arches64[i].cputype,
input->fat_arches64[i].cpusubtype &
~CPU_SUBTYPE_MASK);
}
}
for(i = 0; i < input->fat_header->nfat_arch; i++){
thin = new_thin();
thin->name = input->name;
thin->addr = addr + input->fat_arches64[i].offset;
thin->cputype = input->fat_arches64[i].cputype;
thin->cpusubtype = input->fat_arches64[i].cpusubtype;
thin->offset = input->fat_arches64[i].offset;
thin->size = input->fat_arches64[i].size;
thin->align = input->fat_arches64[i].align;
thin->from_fat = TRUE;
if(input->fat_arches64[i].size >= SARMAG &&
strncmp(thin->addr, ARMAG, SARMAG) == 0)
archives_in_input = TRUE;
}
}
else if(size >= sizeof(struct mach_header) &&
(*((uint32_t *)addr) == MH_MAGIC ||
*((uint32_t *)addr) == SWAP_INT(MH_MAGIC))){
thin = new_thin();
input->is_thin = TRUE;
thin->name = input->name;
thin->addr = addr;
mhp = (struct mach_header *)addr;
lcp = (struct load_command *)((char *)mhp +
sizeof(struct mach_header));
if(mhp->magic == SWAP_INT(MH_MAGIC)){
swapped = TRUE;
mh = *mhp;
swap_mach_header(&mh, get_host_byte_sex());
mhp = &mh;
}
else
swapped = FALSE;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the size "
"field in struct fat_arch is only 32-bits and the size "
"(%llu) of %s exceeds that", size, input->name);
thin->cputype = mhp->cputype;
thin->cpusubtype = mhp->cpusubtype;
thin->offset = 0;
thin->size = size;
if (MH_OBJECT == mhp->filetype)
thin->align = guess_align(getpagesize(), MINSEGALIGN32,
MAXSEGALIGN);
else
thin->align = get_seg_align(mhp, NULL, lcp, swapped, size,
input->name);
if(input->arch_flag.name != NULL)
check_arch(input, thin);
}
else if(size >= sizeof(struct mach_header_64) &&
(*((uint32_t *)addr) == MH_MAGIC_64 ||
*((uint32_t *)addr) == SWAP_INT(MH_MAGIC_64))){
thin = new_thin();
input->is_thin = TRUE;
thin->name = input->name;
thin->addr = addr;
mhp64 = (struct mach_header_64 *)addr;
lcp = (struct load_command *)((char *)mhp64 +
sizeof(struct mach_header_64));
if(mhp64->magic == SWAP_INT(MH_MAGIC_64)){
swapped = TRUE;
mh64 = *mhp64;
swap_mach_header_64(&mh64, get_host_byte_sex());
mhp64 = &mh64;
}
else
swapped = FALSE;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the size "
"field in struct fat_arch is only 32-bits and the size "
"(%llu) of %s exceeds that", size, input->name);
thin->cputype = mhp64->cputype;
thin->cpusubtype = mhp64->cpusubtype;
thin->offset = 0;
thin->size = size;
if (MH_OBJECT == mhp64->filetype)
thin->align = guess_align(getpagesize(), MINSEGALIGN64,
MAXSEGALIGN);
else
thin->align = get_seg_align(NULL, mhp64, lcp, swapped, size,
input->name);
if(input->arch_flag.name != NULL)
check_arch(input, thin);
}
else if(size >= SARMAG && strncmp(addr, ARMAG, SARMAG) == 0){
check_archive(input->name, addr, size, &cputype, &cpusubtype);
archives_in_input = TRUE;
thin = new_thin();
input->is_thin = TRUE;
thin->name = input->name;
thin->addr = addr;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the size "
"field in struct fat_arch is only 32-bits and the size "
"(%llu) of %s exceeds that", size, input->name);
thin->cputype = cputype;
thin->cpusubtype = cpusubtype;
thin->offset = 0;
thin->size = size;
if(thin->cputype & CPU_ARCH_ABI64)
thin->align = 3;
else
thin->align = 2;
if(input->arch_flag.name != NULL){
if(cputype == 0){
thin->cputype = input->arch_flag.cputype;
thin->cpusubtype = input->arch_flag.cpusubtype;
}
else
check_arch(input, thin);
}
else{
if(cputype == 0)
fatal("archive with no architecture specification: %s "
"(can't determine architecture for it)", input->name);
}
}
else{
if(input->arch_flag.name != NULL){
thin = new_thin();
thin->name = input->name;
thin->addr = addr;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the size "
"field in struct fat_arch is only 32-bits and the "
"size (%llu) of %s exceeds that", size, input->name);
thin->cputype = input->arch_flag.cputype;
thin->cpusubtype = input->arch_flag.cpusubtype;
thin->offset = 0;
thin->size = size;
thin->align = 0;
}
else{
#ifdef LTO_SUPPORT
if(is_llvm_bitcode_from_memory(addr, (uint32_t)size,
&input->arch_flag, NULL) != 0){
thin = new_thin();
thin->name = input->name;
thin->addr = addr;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the "
"size field in struct fat_arch is only 32-bits "
"and the size (%llu) of %s exceeds that", size,
input->name);
thin->cputype = input->arch_flag.cputype;
thin->cpusubtype = input->arch_flag.cpusubtype;
thin->offset = 0;
thin->size = size;
thin->align = 0;
}
else
#endif
fatal("can't figure out the architecture type of: %s",
input->name);
}
}
}
static
void
process_replace_file(
struct replace *replace)
{
int fd;
struct stat stat_buf;
uint64_t size;
char *addr;
struct mach_header *mhp, mh;
struct mach_header_64 *mhp64, mh64;
struct load_command *lcp;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
enum bool swapped;
if((fd = open(replace->thin_file.name, O_RDONLY)) == -1)
system_fatal("can't open replacement file: %s",
replace->thin_file.name);
if(fstat(fd, &stat_buf) == -1)
system_fatal("can't stat replacement file: %s",
replace->thin_file.name);
size = stat_buf.st_size;
if((stat_buf.st_mode & S_IFREG) == S_IFREG && size == 0)
addr = NULL;
else
addr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE,
fd, 0);
if((intptr_t)addr == -1)
system_error("can't map replacement file: %s",
replace->thin_file.name);
close(fd);
if(size >= sizeof(struct fat_header) &&
#ifdef __BIG_ENDIAN__
(*((uint32_t *)addr) == FAT_MAGIC ||
*((uint32_t *)addr) == FAT_MAGIC_64))
#endif
#ifdef __LITTLE_ENDIAN__
(*((uint32_t *)addr) == SWAP_INT(FAT_MAGIC) ||
*((uint32_t *)addr) == SWAP_INT(FAT_MAGIC_64)))
#endif
{
fatal("replacement file: %s is a fat file (must be a thin file)",
replace->thin_file.name);
}
else if(size >= sizeof(struct mach_header) &&
(*((uint32_t *)addr) == MH_MAGIC ||
*((uint32_t *)addr) == SWAP_INT(MH_MAGIC))){
replace->thin_file.addr = addr;
mhp = (struct mach_header *)addr;
lcp = (struct load_command *)((char *)mhp +
sizeof(struct mach_header));
if(mhp->magic == SWAP_INT(MH_MAGIC)){
swapped = TRUE;
mh = *mhp;
swap_mach_header(&mh, get_host_byte_sex());
mhp = &mh;
}
else
swapped = FALSE;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the "
"size field in struct fat_arch is only 32-bits "
"and the size (%llu) of %s exceeds that", size,
replace->thin_file.name);
replace->thin_file.cputype = mhp->cputype;
replace->thin_file.cpusubtype = mhp->cpusubtype;
replace->thin_file.offset = 0;
replace->thin_file.size = size;
if (MH_OBJECT == mhp->filetype)
replace->thin_file.align = guess_align(getpagesize(),
MINSEGALIGN32,
MAXSEGALIGN);
else
replace->thin_file.align =
get_seg_align(mhp, NULL, lcp, swapped, size,
replace->thin_file.name);
}
else if(size >= sizeof(struct mach_header_64) &&
(*((uint32_t *)addr) == MH_MAGIC_64 ||
*((uint32_t *)addr) == SWAP_INT(MH_MAGIC_64))){
replace->thin_file.addr = addr;
mhp64 = (struct mach_header_64 *)addr;
lcp = (struct load_command *)((char *)mhp64 +
sizeof(struct mach_header_64));
if(mhp64->magic == SWAP_INT(MH_MAGIC_64)){
swapped = TRUE;
mh64 = *mhp64;
swap_mach_header_64(&mh64, get_host_byte_sex());
mhp64 = &mh64;
}
else
swapped = FALSE;
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the "
"size field in struct fat_arch is only 32-bits "
"and the size (%llu) of %s exceeds that", size,
replace->thin_file.name);
replace->thin_file.cputype = mhp64->cputype;
replace->thin_file.cpusubtype = mhp64->cpusubtype;
replace->thin_file.offset = 0;
replace->thin_file.size = size;
if (MH_OBJECT == mhp64->filetype)
replace->thin_file.align = guess_align(getpagesize(),
MINSEGALIGN64,
MAXSEGALIGN);
else
replace->thin_file.align =
get_seg_align(NULL, mhp64, lcp, swapped, size,
replace->thin_file.name);
}
else if(size >= SARMAG && strncmp(addr, ARMAG, SARMAG) == 0){
check_archive(replace->thin_file.name, addr, size,
&cputype, &cpusubtype);
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the "
"size field in struct fat_arch is only 32-bits "
"and the size (%llu) of %s exceeds that", size,
replace->thin_file.name);
replace->thin_file.addr = addr;
replace->thin_file.cputype = cputype;
replace->thin_file.cpusubtype = cpusubtype;
replace->thin_file.offset = 0;
replace->thin_file.size = size;
replace->thin_file.align = 2;
}
else{
if(fat64_flag == FALSE && size > UINT32_MAX)
fatal("file too large to be in a fat file because the "
"size field in struct fat_arch is only 32-bits "
"and the size (%llu) of %s exceeds that", size,
replace->thin_file.name);
replace->thin_file.addr = addr;
replace->thin_file.cputype = replace->arch_flag.cputype;
replace->thin_file.cpusubtype = replace->arch_flag.cpusubtype;
replace->thin_file.offset = 0;
replace->thin_file.size = size;
replace->thin_file.align = 0;
}
if(replace->thin_file.cputype != replace->arch_flag.cputype ||
(replace->thin_file.cpusubtype & ~CPU_SUBTYPE_MASK) !=
(replace->arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK))
fatal("specified architecture: %s for replacement file: %s does "
"not match the file's architecture", replace->arch_flag.name,
replace->thin_file.name);
}
static
void
check_archive(
char *name,
char *addr,
uint64_t size,
cpu_type_t *cputype,
cpu_subtype_t *cpusubtype)
{
uint64_t offset;
uint32_t magic, i, ar_name_size, ar_size;
struct mach_header mh;
struct mach_header_64 mh64;
struct ar_hdr *ar_hdr;
char *ar_name, *ar_addr;
struct arch_flag arch_flag;
*cputype = 0;
*cpusubtype = 0;
offset = SARMAG;
if(offset == size)
fatal("empty archive with no architecture specification: %s "
"(can't determine architecture for it)", name);
if(offset != size && offset + sizeof(struct ar_hdr) > size)
fatal("truncated or malformed archive: %s (archive header of "
"first member extends past the end of the file)", name);
while(size > offset){
ar_hdr = (struct ar_hdr *)(addr + offset);
offset += sizeof(struct ar_hdr);
if(strncmp(ar_hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0){
check_extend_format_1(name, ar_hdr, size-offset, &ar_name_size);
i = ar_name_size;
ar_name = ar_hdr->ar_name + sizeof(struct ar_hdr);
}
else{
i = size_ar_name(ar_hdr->ar_name);
ar_name = ar_hdr->ar_name;
ar_name_size = 0;
}
if(size + ar_name_size - offset > sizeof(uint32_t)){
memcpy(&magic, addr + offset + ar_name_size,
sizeof(uint32_t));
#ifdef __BIG_ENDIAN__
if(magic == FAT_MAGIC || magic == FAT_MAGIC_64)
#endif
#ifdef __LITTLE_ENDIAN__
if(magic == SWAP_INT(FAT_MAGIC) ||
magic == SWAP_INT(FAT_MAGIC_64))
#endif
fatal("archive member %s(%.*s) is a fat file (not "
"allowed in an archive)", name, (int)i, ar_name);
if((size - ar_name_size) - offset >=
sizeof(struct mach_header) &&
(magic == MH_MAGIC || magic == SWAP_INT(MH_MAGIC))){
memcpy(&mh, addr + offset + ar_name_size,
sizeof(struct mach_header));
if(mh.magic == SWAP_INT(MH_MAGIC))
swap_mach_header(&mh, get_host_byte_sex());
if(*cputype == 0){
*cputype = mh.cputype;
*cpusubtype = mh.cpusubtype;
}
else if(*cputype != mh.cputype){
fatal("archive member %s(%.*s) cputype (%d) and "
"cpusubtype (%d) does not match previous "
"archive members cputype (%d) and cpusubtype"
" (%d) (all members must match)", name,
(int)i, ar_name, mh.cputype, mh.cpusubtype &
~CPU_SUBTYPE_MASK, *cputype, (*cpusubtype) &
~CPU_SUBTYPE_MASK);
}
else {
if (mh.cputype == CPU_TYPE_ARM &&
*cpusubtype != mh.cpusubtype)
fatal("archive member %s(%.*s) cputype (%d) and "
"cpusubtype (%d) does not match previous "
"archive members cputype (%d) and cpusubtype"
" (%d) (all members must match)", name,
(int)i, ar_name, mh.cputype, mh.cpusubtype &
~CPU_SUBTYPE_MASK, *cputype, (*cpusubtype) &
~CPU_SUBTYPE_MASK);
}
}
else if((size - ar_name_size) - offset >=
sizeof(struct mach_header_64) &&
(magic == MH_MAGIC_64 || magic == SWAP_INT(MH_MAGIC_64))){
memcpy(&mh64, addr + offset + ar_name_size,
sizeof(struct mach_header_64));
if(mh64.magic == SWAP_INT(MH_MAGIC_64))
swap_mach_header_64(&mh64, get_host_byte_sex());
if(*cputype == 0){
*cputype = mh64.cputype;
*cpusubtype = mh64.cpusubtype;
}
else if(*cputype != mh64.cputype){
fatal("archive member %s(%.*s) cputype (%d) and "
"cpusubtype (%d) does not match previous "
"archive members cputype (%d) and cpusubtype"
" (%d) (all members must match)", name,
(int)i, ar_name, mh64.cputype, mh64.cpusubtype &
~CPU_SUBTYPE_MASK, *cputype, (*cpusubtype) &
~CPU_SUBTYPE_MASK);
}
else {
if ((mh64.cputype == CPU_TYPE_X86_64 ||
mh64.cputype == CPU_TYPE_ARM64) &&
*cpusubtype != mh64.cpusubtype)
fatal("archive member %s(%.*s) cputype (%d) and "
"cpusubtype (%d) does not match previous "
"archive members cputype (%d) and cpusubtype"
" (%d) (all members must match)", name,
(int)i, ar_name, mh64.cputype, mh64.cpusubtype &
~CPU_SUBTYPE_MASK, *cputype, (*cpusubtype) &
~CPU_SUBTYPE_MASK);
}
}
else{
if(strncmp(ar_name, SYMDEF, sizeof(SYMDEF) - 1) != 0){
ar_addr = addr + offset + ar_name_size;
ar_size = (uint32_t)strtoul(ar_hdr->ar_size, NULL, 10);
#ifdef LTO_SUPPORT
if(is_llvm_bitcode_from_memory(ar_addr, ar_size,
&arch_flag, NULL) != 0){
if(*cputype == 0){
*cputype = arch_flag.cputype;
*cpusubtype = arch_flag.cpusubtype;
}
else if(*cputype != arch_flag.cputype){
fatal("archive member %s(%.*s) cputype (%d) "
"and cpusubtype (%d) does not match "
"previous archive members cputype (%d) "
"and cpusubtype (%d) (all members must "
"match)", name, (int)i, ar_name,
arch_flag.cputype, arch_flag.cpusubtype &
~CPU_SUBTYPE_MASK, *cputype,
(*cpusubtype) & ~CPU_SUBTYPE_MASK);
}
}
#endif
}
}
}
offset += rnd64(strtoul(ar_hdr->ar_size, NULL, 10),
sizeof(short));
}
}
static
void
check_extend_format_1(
char *name,
struct ar_hdr *ar_hdr,
uint64_t size_left,
uint32_t *member_name_size)
{
char *p, *endp, buf[sizeof(ar_hdr->ar_name)+1];
uint32_t ar_name_size;
*member_name_size = 0;
buf[sizeof(ar_hdr->ar_name)] = '\0';
memcpy(buf, ar_hdr->ar_name, sizeof(ar_hdr->ar_name));
p = buf + sizeof(AR_EFMT1) - 1;
if(isdigit(*p) == 0)
fatal("archive: %s malformed (ar_name: %.*s for archive extend "
"format #1 starts with non-digit)", name,
(int)sizeof(ar_hdr->ar_name), ar_hdr->ar_name);
ar_name_size = (uint32_t)strtoul(p, &endp, 10);
if(ar_name_size == UINT_MAX && errno == ERANGE)
fatal("archive: %s malformed (size in ar_name: %.*s for archive "
"extend format #1 overflows uint32_t)", name,
(int)sizeof(ar_hdr->ar_name), ar_hdr->ar_name);
while(*endp == ' ' && *endp != '\0')
endp++;
if(*endp != '\0')
fatal("archive: %s malformed (size in ar_name: %.*s for archive "
"extend format #1 contains non-digit and non-space "
"characters)", name, (int)sizeof(ar_hdr->ar_name),
ar_hdr->ar_name);
if(ar_name_size > size_left)
fatal("archive: %s truncated or malformed (archive name "
"of member extends past the end of the file)", name);
*member_name_size = ar_name_size;
}
static uint32_t get_mh_filetype(
char* addr,
uint64_t size)
{
uint32_t filetype;
uint32_t magic;
struct mach_header mh32;
struct mach_header_64 mh64;
filetype = 0;
if (size >= sizeof(magic)) {
magic = *(uint32_t*)(addr);
if (magic == MH_MAGIC || magic == MH_CIGAM) {
if (size >= sizeof(mh32)) {
memcpy(&mh32, addr, sizeof(mh32));
if (magic == MH_CIGAM)
swap_mach_header(&mh32, get_host_byte_sex());
filetype = mh32.filetype;
}
}
else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) {
if (size >= sizeof(mh64)) {
memcpy(&mh64, addr, sizeof(mh64));
if (magic == MH_CIGAM)
swap_mach_header_64(&mh64, get_host_byte_sex());
filetype = mh64.filetype;
}
}
}
return filetype;
}
static
void
print_arch(
cpu_type_t cputype,
cpu_subtype_t cpusubtype)
{
switch(cputype){
case CPU_TYPE_MC680x0:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_MC680x0_ALL:
printf("m68k");
break;
case CPU_SUBTYPE_MC68030_ONLY:
printf("m68030");
break;
case CPU_SUBTYPE_MC68040:
printf("m68040");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_POWERPC:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_POWERPC_ALL:
printf("ppc");
break;
case CPU_SUBTYPE_POWERPC_601:
printf("ppc601");
break;
case CPU_SUBTYPE_POWERPC_603:
printf("ppc603");
break;
case CPU_SUBTYPE_POWERPC_603e:
printf("ppc603e");
break;
case CPU_SUBTYPE_POWERPC_603ev:
printf("ppc603ev");
break;
case CPU_SUBTYPE_POWERPC_604:
printf("ppc604");
break;
case CPU_SUBTYPE_POWERPC_604e:
printf("ppc604e");
break;
case CPU_SUBTYPE_POWERPC_750:
printf("ppc750");
break;
case CPU_SUBTYPE_POWERPC_7400:
printf("ppc7400");
break;
case CPU_SUBTYPE_POWERPC_7450:
printf("ppc7450");
break;
case CPU_SUBTYPE_POWERPC_970:
printf("ppc970");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_POWERPC64:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_POWERPC_ALL:
printf("ppc64");
break;
case CPU_SUBTYPE_POWERPC_970:
printf("ppc970-64");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_VEO:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_VEO_1:
printf("veo1");
break;
case CPU_SUBTYPE_VEO_2:
printf("veo2");
break;
case CPU_SUBTYPE_VEO_3:
printf("veo3");
break;
case CPU_SUBTYPE_VEO_4:
printf("veo4");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_MC88000:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_MC88000_ALL:
case CPU_SUBTYPE_MC88110:
printf("m88k");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_I386:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_I386_ALL:
printf("i386");
break;
case CPU_SUBTYPE_486:
printf("i486");
break;
case CPU_SUBTYPE_486SX:
printf("i486SX");
break;
case CPU_SUBTYPE_PENT:
printf("pentium");
break;
case CPU_SUBTYPE_PENTPRO:
printf("pentpro");
break;
case CPU_SUBTYPE_PENTII_M3:
printf("pentIIm3");
break;
case CPU_SUBTYPE_PENTII_M5:
printf("pentIIm5");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_X86_64:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_X86_64_ALL:
printf("x86_64");
break;
case CPU_SUBTYPE_X86_64_H:
printf("x86_64h");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_I860:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_I860_ALL:
case CPU_SUBTYPE_I860_860:
printf("i860");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_HPPA:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_HPPA_ALL:
case CPU_SUBTYPE_HPPA_7100LC:
printf("hppa");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_SPARC:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_SPARC_ALL:
printf("sparc");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ARM:
switch(cpusubtype){
case CPU_SUBTYPE_ARM_ALL:
printf("arm");
break;
case CPU_SUBTYPE_ARM_V4T:
printf("armv4t");
break;
case CPU_SUBTYPE_ARM_V5TEJ:
printf("armv5");
break;
case CPU_SUBTYPE_ARM_XSCALE:
printf("xscale");
break;
case CPU_SUBTYPE_ARM_V6:
printf("armv6");
break;
case CPU_SUBTYPE_ARM_V6M:
printf("armv6m");
break;
case CPU_SUBTYPE_ARM_V7:
printf("armv7");
break;
case CPU_SUBTYPE_ARM_V7F:
printf("armv7f");
break;
case CPU_SUBTYPE_ARM_V7S:
printf("armv7s");
break;
case CPU_SUBTYPE_ARM_V7K:
printf("armv7k");
break;
case CPU_SUBTYPE_ARM_V7M:
printf("armv7m");
break;
case CPU_SUBTYPE_ARM_V7EM:
printf("armv7em");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ARM64:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_ARM64_ALL:
printf("arm64");
break;
case CPU_SUBTYPE_ARM64_V8:
printf("arm64v8");
break;
case CPU_SUBTYPE_ARM64E:
printf("arm64e");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ARM64_32:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_ARM64_32_V8:
printf("arm64_32");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ANY:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_MULTIPLE:
printf("any");
break;
case CPU_SUBTYPE_LITTLE_ENDIAN:
printf("little");
break;
case CPU_SUBTYPE_BIG_ENDIAN:
printf("big");
break;
default:
goto print_arch_unknown;
}
break;
print_arch_unknown:
default:
printf("(cputype (%d) cpusubtype (%d))", cputype,
cpusubtype & ~CPU_SUBTYPE_MASK);
break;
}
}
static
void
print_cputype(
cpu_type_t cputype,
cpu_subtype_t cpusubtype)
{
cpu_subtype_t rawsubtype;
rawsubtype = cpusubtype;
cpusubtype &= ~CPU_SUBTYPE_MASK;
switch(cputype){
case CPU_TYPE_MC680x0:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_MC680x0_ALL:
printf(" cputype CPU_TYPE_MC680x0\n"
" cpusubtype CPU_SUBTYPE_MC680x0_ALL\n");
break;
case CPU_SUBTYPE_MC68030_ONLY:
printf(" cputype CPU_TYPE_MC680x0\n"
" cpusubtype CPU_SUBTYPE_MC68030_ONLY\n");
break;
case CPU_SUBTYPE_MC68040:
printf(" cputype CPU_TYPE_MC680x0\n"
" cpusubtype CPU_SUBTYPE_MC68040\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_POWERPC:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_POWERPC_ALL:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_ALL\n");
break;
case CPU_SUBTYPE_POWERPC_601:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_601\n");
break;
case CPU_SUBTYPE_POWERPC_603:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_603\n");
break;
case CPU_SUBTYPE_POWERPC_603e:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_603e\n");
break;
case CPU_SUBTYPE_POWERPC_603ev:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_603ev\n");
break;
case CPU_SUBTYPE_POWERPC_604:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_604\n");
break;
case CPU_SUBTYPE_POWERPC_604e:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_604e\n");
break;
case CPU_SUBTYPE_POWERPC_750:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_750\n");
break;
case CPU_SUBTYPE_POWERPC_7400:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_7400\n");
break;
case CPU_SUBTYPE_POWERPC_7450:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_7450\n");
break;
case CPU_SUBTYPE_POWERPC_970:
printf(" cputype CPU_TYPE_POWERPC\n"
" cpusubtype CPU_SUBTYPE_POWERPC_970\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_POWERPC64:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_POWERPC_ALL:
printf(" cputype CPU_TYPE_POWERPC64\n"
" cpusubtype CPU_SUBTYPE_POWERPC_ALL\n");
break;
case CPU_SUBTYPE_POWERPC_970:
printf(" cputype CPU_TYPE_POWERPC64\n"
" cpusubtype CPU_SUBTYPE_POWERPC_970\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_VEO:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_VEO_1:
printf(" cputype CPU_TYPE_VEO\n"
" cpusubtype CPU_SUBTYPE_VEO_1\n");
break;
case CPU_SUBTYPE_VEO_2:
printf(" cputype CPU_TYPE_VEO\n"
" cpusubtype CPU_SUBTYPE_VEO_2\n");
break;
case CPU_SUBTYPE_VEO_3:
printf(" cputype CPU_TYPE_VEO\n"
" cpusubtype CPU_SUBTYPE_VEO_3\n");
break;
case CPU_SUBTYPE_VEO_4:
printf(" cputype CPU_TYPE_VEO\n"
" cpusubtype CPU_SUBTYPE_VEO_4\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_MC88000:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_MC88000_ALL:
printf(" cputype CPU_TYPE_MC88000\n"
" cpusubtype CPU_SUBTYPE_MC88000_ALL\n");
break;
case CPU_SUBTYPE_MC88110:
printf(" cputype CPU_TYPE_MC88000\n"
" cpusubtype CPU_SUBTYPE_MC88110\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_I386:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_I386_ALL:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_I386_ALL\n");
break;
case CPU_SUBTYPE_486:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_486\n");
break;
case CPU_SUBTYPE_486SX:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_486SX\n");
break;
case CPU_SUBTYPE_PENT:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_PENT\n");
break;
case CPU_SUBTYPE_PENTPRO:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_PENTPRO\n");
break;
case CPU_SUBTYPE_PENTII_M3:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_PENTII_M3\n");
break;
case CPU_SUBTYPE_PENTII_M5:
printf(" cputype CPU_TYPE_I386\n"
" cpusubtype CPU_SUBTYPE_PENTII_M5\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_X86_64:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_X86_64_ALL:
printf(" cputype CPU_TYPE_X86_64\n"
" cpusubtype CPU_SUBTYPE_X86_64_ALL\n");
break;
case CPU_SUBTYPE_X86_64_H:
printf(" cputype CPU_TYPE_X86_64\n"
" cpusubtype CPU_SUBTYPE_X86_64_H\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_I860:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_I860_ALL:
printf(" cputype CPU_TYPE_I860\n"
" cpusubtype CPU_SUBTYPE_I860_ALL\n");
break;
case CPU_SUBTYPE_I860_860:
printf(" cputype CPU_TYPE_I860\n"
" cpusubtype CPU_SUBTYPE_I860_860\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_HPPA:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_HPPA_ALL:
printf(" cputype CPU_TYPE_HPPA\n"
" cpusubtype CPU_SUBTYPE_HPPA_ALL\n");
break;
case CPU_SUBTYPE_HPPA_7100LC:
printf(" cputype CPU_TYPE_HPPA\n"
" cpusubtype CPU_SUBTYPE_HPPA_7100LC\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_SPARC:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_SPARC_ALL:
printf(" cputype CPU_TYPE_SPARC\n"
" cpusubtype CPU_SUBTYPE_SPARC_ALL\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ARM:
switch(cpusubtype){
case CPU_SUBTYPE_ARM_V4T:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V4T\n");
break;
case CPU_SUBTYPE_ARM_V5TEJ:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V5TEJ\n");
break;
case CPU_SUBTYPE_ARM_XSCALE:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_XSCALE\n");
break;
case CPU_SUBTYPE_ARM_V6:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V6\n");
break;
case CPU_SUBTYPE_ARM_V6M:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V6M\n");
break;
case CPU_SUBTYPE_ARM_V7:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V7\n");
break;
case CPU_SUBTYPE_ARM_V7F:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V7F\n");
break;
case CPU_SUBTYPE_ARM_V7S:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V7S\n");
break;
case CPU_SUBTYPE_ARM_V7K:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V7K\n");
break;
case CPU_SUBTYPE_ARM_V7M:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V7M\n");
break;
case CPU_SUBTYPE_ARM_V7EM:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_V7EM\n");
break;
case CPU_SUBTYPE_ARM_ALL:
printf(" cputype CPU_TYPE_ARM\n"
" cpusubtype CPU_SUBTYPE_ARM_ALL\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ARM64:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_ARM64_ALL:
printf(" cputype CPU_TYPE_ARM64\n"
" cpusubtype CPU_SUBTYPE_ARM64_ALL\n");
break;
case CPU_SUBTYPE_ARM64_V8:
printf(" cputype CPU_TYPE_ARM64\n"
" cpusubtype CPU_SUBTYPE_ARM64_V8\n");
break;
case CPU_SUBTYPE_ARM64E:
printf(" cputype CPU_TYPE_ARM64\n"
" cpusubtype CPU_SUBTYPE_ARM64E\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ARM64_32:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_ARM64_32_V8:
printf(" cputype CPU_TYPE_ARM64_32\n"
" cpusubtype CPU_SUBTYPE_ARM64_32_V8\n");
break;
default:
goto print_arch_unknown;
}
break;
case CPU_TYPE_ANY:
switch(cpusubtype & ~CPU_SUBTYPE_MASK){
case CPU_SUBTYPE_MULTIPLE:
printf(" cputype CPU_TYPE_ANY\n"
" cpusubtype CPU_SUBTYPE_MULTIPLE\n");
break;
case CPU_SUBTYPE_LITTLE_ENDIAN:
printf(" cputype CPU_TYPE_ANY\n"
" cpusubtype CPU_SUBTYPE_LITTLE_ENDIAN\n");
break;
case CPU_SUBTYPE_BIG_ENDIAN:
printf(" cputype CPU_TYPE_ANY\n"
" cpusubtype CPU_SUBTYPE_BIG_ENDIAN\n");
break;
default:
goto print_arch_unknown;
}
break;
print_arch_unknown:
default:
printf(" cputype (%d)\n"
" cpusubtype cpusubtype (%d)\n", cputype,
cpusubtype & ~CPU_SUBTYPE_MASK);
break;
}
if (detailed_info_flag) {
if ((cputype == CPU_TYPE_X86_64 || cputype == CPU_TYPE_POWERPC64) &&
((rawsubtype & CPU_SUBTYPE_MASK) == CPU_SUBTYPE_LIB64))
printf(" capabilities CPU_SUBTYPE_LIB64\n");
else if (cputype == CPU_TYPE_ARM64 &&
(rawsubtype & CPU_SUBTYPE_ARM64E_VERSIONED_ABI_MASK)) {
printf(" capabilities PTR_AUTH_VERSION");
if (rawsubtype & CPU_SUBTYPE_ARM64E_KERNEL_ABI_MASK)
printf(" KERNEL");
else
printf(" USERSPACE");
printf(" %d\n",
rawsubtype & CPU_SUBTYPE_ARM64E_KERNEL_ABI_MASK >> 24);
}
else
printf(" capabilities 0x%x\n", (unsigned int)
((rawsubtype & CPU_SUBTYPE_MASK) >> 24));
}
}
static
int
size_ar_name(
char *ar_name)
{
uint32_t j;
struct ar_hdr ar_hdr;
for(j = 0; j < sizeof(ar_hdr.ar_name); j++){
if(ar_name[j] == ' ')
break;
}
return(j);
}
static
struct input_file *
new_input(void)
{
struct input_file *input;
input_files = reallocate(input_files,
(ninput_files + 1) * sizeof(struct input_file));
input = input_files + ninput_files;
ninput_files++;
memset(input, '\0', sizeof(struct input_file));
return(input);
}
static
struct thin_file *
new_thin(void)
{
struct thin_file *thin;
thin_files = reallocate(thin_files,
(nthin_files + 1) * sizeof(struct thin_file));
thin = thin_files + nthin_files;
nthin_files++;
memset(thin, '\0', sizeof(struct thin_file));
return(thin);
}
static
struct arch_flag *
new_arch_flag(
struct arch_flag **arch_flags,
uint32_t *narch_flags)
{
struct arch_flag *arch_flag;
*arch_flags = reallocate(*arch_flags,
(*narch_flags + 1) * sizeof(struct arch_flag));
arch_flag = *arch_flags + *narch_flags;
*narch_flags = *narch_flags + 1;
memset(arch_flag, '\0', sizeof(struct arch_flag));
return(arch_flag);
}
static
struct replace *
new_replace(void)
{
struct replace *replace;
replaces = reallocate(replaces,
(nreplaces + 1) * sizeof(struct replace));
replace = replaces + nreplaces;
nreplaces++;
memset(replace, '\0', sizeof(struct replace));
return(replace);
}
static
struct segalign *
new_segalign(void)
{
struct segalign *segalign;
segaligns = reallocate(segaligns,
(nsegaligns + 1) * sizeof(struct segalign));
segalign = segaligns + nsegaligns;
nsegaligns++;
memset(segalign, '\0', sizeof(struct segalign));
return(segalign);
}
static
int
cmp_qsort(
const struct thin_file *thin1,
const struct thin_file *thin2)
{
if (thin1->cputype == thin2->cputype)
return ((thin1->cpusubtype & ~CPU_SUBTYPE_MASK) -
(thin2->cpusubtype & ~CPU_SUBTYPE_MASK));
if (thin1->cputype == CPU_TYPE_ARM64)
return 1;
if (thin2->cputype == CPU_TYPE_ARM64)
return -1;
return thin1->align - thin2->align;
}
static
enum
bool
ispoweroftwo(
uint32_t x)
{
if(x == 0)
return(TRUE);
while((x & 0x1) != 0x1){
x >>= 1;
}
if((x & ~0x1) != 0)
return(FALSE);
else
return(TRUE);
}
static
void
check_arch(
struct input_file *input,
struct thin_file *thin)
{
if(input->arch_flag.cputype != thin->cputype)
fatal("specifed architecture type (%s) for file (%s) does "
"not match its cputype (%d) and cpusubtype (%d) "
"(should be cputype (%d) and cpusubtype (%d))",
input->arch_flag.name, input->name,
thin->cputype, thin->cpusubtype &
~CPU_SUBTYPE_MASK, input->arch_flag.cputype,
input->arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK);
}
static
void
usage(void)
{
fprintf(stderr,
"usage: lipo <input_file> <command> [<options> ...]\n"
" command is one of:\n"
" -archs\n"
" -create\n"
" -detailed_info\n"
" -extract <arch_type> [-extract <arch_type> ...]\n"
" -extract_family <arch_type> [-extract_family <arch_type> ...]\n"
" -info\n"
" -remove <arch_type> [-remove <arch_type> ...]\n"
" -replace <arch_type> <file_name> [-replace <arch_type> <file_name> ...]\n"
" -thin <arch_type>\n"
" -verify_arch <arch_type> ...\n"
" options are one or more of:\n"
" -arch <arch_type> <input_file>\n"
" -hideARM64\n"
" -output <output_file>\n"
" -segalign <arch_type> <alignment>\n"
);
exit(EXIT_FAILURE);
}