#include #include #include #include #define UNKNOWN_COMMAND 0 #define CREATE_COMMAND 1 #define REMOVE_COMMAND 2 #define THIN_COMMAND 3 #define REPLACE_COMMAND 4 #define EXTRACT_COMMAND 5 #define INFO_COMMAND 6 #define DINFO_COMMAND 7 #define be32toh(x) x #define htobe32(x) x #define le16toh(x) OSSwapLittleToHostInt16(x) struct fat_header { uint32_t magic; uint32_t nfat_arch; }; struct arch { uint32_t cputype; uint32_t cpusubtype; uint32_t offset; uint32_t size; uint32_t align; }; struct elf_header{ unsigned char ident[16]; uint16_t type; uint16_t machine; uint32_t version; uint32_t entry; uint32_t phoff; uint32_t shoff; uint32_t flags; uint16_t ehsize; uint16_t phentsize; uint16_t phnum; uint16_t shentsize; uint16_t shnum; uint16_t shstrndx; }; int extract(char *ofile, char *ifile, int arch_type); int replace(char *ofile, char *ifile, int arch_type); int thin(char *ofile, char *ifile, int arch_type); int remove_file(char *ofile, char *ifile, int arch_type); int create(char *ofile, char *ifiles[]); int info(char *ifile); int detailed_info(char *ifile); uint32_t copy(FILE *ofp, FILE *ifp, uint32_t offset); int identify(FILE *fp, uint32_t *cputype, uint32_t *cpusubtype); char *get_arch_name(uint32_t arch); void usage(void) { printf("lipo -info \n"); printf("lipo -detailed_info \n"); printf("lipo -output -create ...\n"); printf("lipo -output -remove \n"); printf("lipo -output -thin \n"); printf("lipo -output -replace \n"); printf("lipo -output -extract \n"); exit(1); } int main(int argc, char *argv[]) { char *ofile = NULL; char *ifile = NULL; int arch_type = -1; int command = UNKNOWN_COMMAND; int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-output") == 0) { ofile = argv[++i]; continue; } else if (strcmp(argv[i], "-info") == 0) { if (argc != 3) usage(); command = INFO_COMMAND; ifile = argv[++i]; } else if (strcmp(argv[i], "-detailed_info") == 0) { if (argc != 3) usage(); command = DINFO_COMMAND; ifile = argv[++i]; } else if (strcmp(argv[i], "-create") == 0) { if (command != UNKNOWN_COMMAND) usage(); command = CREATE_COMMAND; } else if (strcmp(argv[i], "-remove") == 0) { if (command != UNKNOWN_COMMAND) usage(); command = REMOVE_COMMAND; arch_type = atoi(argv[++i]); ifile = argv[++i]; } else if (strcmp(argv[i], "-thin") == 0) { if (command != UNKNOWN_COMMAND) usage(); command = THIN_COMMAND; arch_type = atoi(argv[++i]); } else if (strcmp(argv[i], "-replace") == 0) { if (command != UNKNOWN_COMMAND) usage(); command = REPLACE_COMMAND; arch_type = atoi(argv[++i]); ifile = argv[++i]; } else if (strcmp(argv[i], "-extract") == 0) { if (command != UNKNOWN_COMMAND) usage(); command = EXTRACT_COMMAND; arch_type = atoi(argv[++i]); } else { if (command == CREATE_COMMAND) break; usage(); } } switch (command) { case INFO_COMMAND: if (ifile == NULL) usage(); return info(ifile); break; case DINFO_COMMAND: if (ifile == NULL) usage(); return detailed_info(ifile); break; case CREATE_COMMAND: if (ofile == NULL) usage(); return create(ofile, &argv[i]); break; case REMOVE_COMMAND: if (ifile == NULL || ofile == NULL) usage(); return remove_file(ofile, ifile, arch_type); break; case THIN_COMMAND: if (ifile == NULL || ofile == NULL) usage(); return thin(ofile, ifile, arch_type); break; case REPLACE_COMMAND: if (ifile == NULL || ofile == NULL) usage(); return replace(ofile, ifile, arch_type); break; case EXTRACT_COMMAND: if (ifile == NULL || ofile == NULL) usage(); return extract(ofile, ifile, arch_type); break; default: usage(); } return 1; } int extract(char *ofile, char *ifile, int arch_type) { printf("NOT IMPLEMENTED\n"); return 1; } int replace(char *ofile, char *ifile, int arch_type) { printf("NOT IMPLEMENTED\n"); return 1; } int thin(char *ofile, char *ifile, int arch_type) { printf("NOT IMPLEMENTED\n"); return 1; } int remove_file(char *ofile, char *ifile, int arch_type) { printf("NOT IMPLEMENTED\n"); return 1; } int create(char *ofile, char *ifiles[]) { char **p = ifiles; struct fat_header hdr; struct arch arch; uint32_t offset = 4096; uint32_t narch = 0; int n; FILE *ofp; ofp = fopen(ofile, "w+"); if (ofp == NULL) errx(1, "Cannot open '%s' for writing", ofile); while (*p) { uint32_t cputype, cpusubtype; uint32_t size; int rv; FILE *ifp; ifp = fopen(*p, "rb"); if (ifp == NULL) errx(1, "Cannot open '%s' for reading", *p); if (identify(ifp, &cputype, &cpusubtype) != 0) errx(1, "unable to determine cpu type"); size = copy(ofp, ifp, offset); if (size == 0) errx(1, "unable to copy contents"); arch.cputype = htobe32(cputype); arch.cpusubtype = htobe32(cpusubtype); arch.offset = htobe32(offset); arch.size = htobe32(size); arch.align = htobe32(12); rv = fseek(ofp, narch * sizeof(struct arch) + sizeof(struct fat_header), SEEK_SET); if (rv != 0) errx(1, "fseek"); n = fwrite(&arch, sizeof(struct arch), 1, ofp); if (n != 1) errx(1, "Failed to write arch record"); fclose(ifp); size += 4096; size &= ~(4096-1); offset += size; narch++; p++; } hdr.magic = htobe32(0xcafebabe); hdr.nfat_arch = htobe32(narch); fseek(ofp, 0, SEEK_SET); n = fwrite(&hdr, sizeof(struct fat_header), 1, ofp); if (n != 1) errx(1, "Cannot write fat header"); fclose(ofp); return 0; } int info(char *ifile) { struct fat_header hdr; struct arch arch; int i, n; FILE *fp = fopen(ifile, "rb"); if (fp == NULL) errx(1, "unable to open file '%s'", ifile); n = fread(&hdr, sizeof(struct fat_header), 1, fp); if (n != 1) errx(1, "unable to read fat header"); if (be32toh(hdr.magic) != 0xcafebabe) errx(1, "Non-fat file"); printf("Architectures in the fat file: %s are: ", ifile); for (i = 0; i < be32toh(hdr.nfat_arch); i++) { n = fread(&arch, sizeof(struct arch), 1, fp); if (n != 1) break; printf("%s ", get_arch_name(be32toh(arch.cputype))); } printf("\n"); fclose(fp); return 0; } int detailed_info(char *ifile) { struct fat_header hdr; struct arch arch; int i, n; FILE *fp = fopen(ifile, "rb"); if (fp == NULL) errx(1, "unable to open file '%s'", ifile); n = fread(&hdr, sizeof(struct fat_header), 1, fp); if (n != 1) errx(1, "unable to read fat header"); if (be32toh(hdr.magic) != 0xcafebabe) errx(1, "Non-fat file"); printf("Fat header in: %s\n", ifile); printf("fat_magic 0x%08x\n", be32toh(hdr.magic)); printf("nfat_arch %d\n", be32toh(hdr.nfat_arch)); for (i = 0; i < be32toh(hdr.nfat_arch); i++) { n = fread(&arch, sizeof(struct arch), 1, fp); if (n != 1) break; printf("architecture %s\n", get_arch_name(be32toh(arch.cputype))); printf("\tcputype 0x%x\n", be32toh(arch.cputype)); printf("\tcpusubtype 0x%x\n", be32toh(arch.cpusubtype)); printf("\toffset %d\n", be32toh(arch.offset)); printf("\tsize %d\n", be32toh(arch.size)); printf("\talign 2^%d (%d)\n", be32toh(arch.align), 1 << be32toh(arch.align)); } fclose(fp); return 0; } uint32_t copy(FILE *ofp, FILE *ifp, uint32_t offset) { char buf[4096]; char zero = 0; uint32_t size = 0; int padding = 0; fseek(ifp, 0, SEEK_SET); fseek(ofp, offset, SEEK_SET); for (;;) { int nr = fread(buf, 1, 4096, ifp); int nw = fwrite(buf, 1, nr, ofp); if (nw != nr) errx(1, "Unable to copy block"); size += nw; if (nw != 4096) break; } padding = 4096 - (size & (4096-1)); printf("padding=%d\n", padding); while (padding-- > 0) if (fwrite(&zero, 1, 1, ofp) != 1) errx(1, "cannot write padding"); return size; } int identify(FILE *fp, uint32_t *cputype, uint32_t *cpusubtype) { struct elf_header hdr; int n; fseek(fp, 0, SEEK_SET); n = fread(&hdr, sizeof(struct elf_header), 1, fp); if (n != 1) return 1; printf("machine=%d (0x%x)\n", le16toh(hdr.machine), le16toh(hdr.machine)); #define LP64 0x1000000 switch (le16toh(hdr.machine)) { case 20: /* ppc */ *cputype = 18; *cpusubtype = 0; break; case 3: /* i386 */ *cputype = 7; *cpusubtype = 3; break; case 62: /* amd642 */ *cputype = LP64 | 7; *cpusubtype = 3; break; case 40: *cputype = 12; *cpusubtype = 0; break; default: return 1; } return 0; } char * get_arch_name(uint32_t arch) { switch (arch) { case 7: return "i386"; case 18: return "ppc"; case 0x1000007: return "x86_64"; case 12: return "arm"; } return "unknown"; }