/* * libMatch.c * F-Script Anywhere * * Created by Nicholas Riley on Fri Feb 01 2002. * Copyright (c) 2001 Nicholas Riley. All rights reserved. * */ /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ // Yes, this file is a gigantic hack of otool code. It still took me several hours // of work to get to this point, though, and all to get around one of Apple's bugs. // Cleanup very much appreciated. #include "libMatch.h" #include #include #include //#include "stuff/bytesex.h" #include "stuff/ofile.h" typedef char BOOL; #define YES (BOOL)1 #define NO (BOOL)0 char *progname = "libMatch"; // used by libstuff for errors #define error printf #define printf NOOP void NOOP(char *fmt, ...) { } BOOL ofile_matches_lib(struct mach_header *mh, struct load_command *load_commands, enum byte_sex load_commands_byte_sex, enum bool just_id, const char **libList); // derived from main.c processor() in otool static BOOL processor( struct ofile *ofile, char *arch_name, const char *libs[]) { char *addr; unsigned long size, magic; struct mach_header mh; struct load_command *load_commands; unsigned long nsorted_symbols; struct nlist *allocated_symbols, *sorted_symbols; unsigned long *indirect_symbols, *allocated_indirect_symbols, nindirect_symbols; struct dylib_module *allocated_mods; struct dylib_table_of_contents *allocated_tocs; struct dylib_reference *allocated_refs; struct twolevel_hint *hints, *allocated_hints; unsigned long nhints; BOOL result; sorted_symbols = NULL; nsorted_symbols = 0; indirect_symbols = NULL; nindirect_symbols = 0; hints = NULL; nhints = 0; /* * These may or may not be allocated. If allocated they will not be * NULL and then free'ed before returning. */ load_commands = NULL; allocated_symbols = NULL; sorted_symbols = NULL; allocated_indirect_symbols = NULL; allocated_tocs = NULL; allocated_mods = NULL; allocated_refs = NULL; allocated_hints = NULL; /* * Print header for the object name if in an archive or an architecture * name is passed in. */ if(TRUE){ // XXX needed? printf("%s", ofile->file_name); if(ofile->member_ar_hdr != NULL){ printf("(%.*s)", (int)ofile->member_name_size, ofile->member_name); } if(arch_name != NULL) printf(" (architecture %s):", arch_name); else printf(":"); /* * If the mach_header pointer is NULL the file is not an object * file. Truncated object file (where the file size is less * than sizeof(struct mach_header)) also does not have it's * mach_header set. So deal with both cases here and then * return as the rest of this routine deals only with things * in object files. */ if(ofile->mh == NULL){ if(ofile->file_type == OFILE_FAT){ /* * This routine is not called on fat files where the * offset is past end of file. An error message is * printed in ofile_specific_arch() in ofile.c. */ if(ofile->arch_type == OFILE_ARCHIVE){ addr = ofile->member_addr; size = ofile->member_size; } else{ addr = ofile->file_addr + ofile->fat_archs[ofile->narch].offset; size = ofile->fat_archs[ofile->narch].size; } if(addr + size > ofile->file_addr + ofile->file_size) size = (ofile->file_addr + ofile->file_size) - addr; } else if(ofile->file_type == OFILE_ARCHIVE){ addr = ofile->member_addr; size = ofile->member_size; } else{ /* ofile->file_type == OFILE_UNKNOWN */ addr = ofile->file_addr; size = ofile->file_size; } if(size > sizeof(long)){ memcpy(&magic, addr, sizeof(unsigned long)); if(magic == MH_MAGIC || magic == SWAP_LONG(MH_MAGIC)){ printf(" is a truncated object file\n"); memset(&mh, '\0', sizeof(struct mach_header)); if(size > sizeof(struct mach_header)) size = sizeof(struct mach_header); memcpy(&mh, addr, size); if(magic == SWAP_LONG(MH_MAGIC)) swap_mach_header(&mh, get_host_byte_sex()); return FALSE; } } printf(" is not an object file\n"); return FALSE; } } if(ofile->mh != NULL){ if((int)(ofile->mh) % sizeof(unsigned long)){ if(TRUE) // XXX remove? printf("(object file offset is not a multiple of sizeof(" "unsigned long))"); memcpy(&mh, ofile->mh, sizeof(struct mach_header)); if(mh.magic == SWAP_LONG(MH_MAGIC)) swap_mach_header(&mh, get_host_byte_sex()); ofile->mh = &mh; } else if(ofile->mh->magic == SWAP_LONG(MH_MAGIC)){ mh = *(ofile->mh); swap_mach_header(&mh, get_host_byte_sex()); ofile->mh = &mh; } } printf("\n"); /* * If this is not an object file then just return. */ if(ofile->mh == NULL) return NO; /* * Calculate the true number of bytes of the of the object file that * is in memory (in case this file is truncated). */ addr = ofile->object_addr; size = ofile->object_size; if(addr + size > ofile->file_addr + ofile->file_size) size = (ofile->file_addr + ofile->file_size) - addr; /* * Load commands. */ if(ofile->mh->sizeofcmds + sizeof(struct mach_header) > size){ load_commands = malloc(ofile->mh->sizeofcmds); memset(load_commands, '\0', ofile->mh->sizeofcmds); memcpy(load_commands, ofile->load_commands, size - sizeof(struct mach_header)); ofile->load_commands = load_commands; } result = ofile_matches_lib(ofile->mh, ofile->load_commands, ofile->object_byte_sex, FALSE, libs); if(load_commands != NULL) free(load_commands); if(allocated_symbols != NULL) free(allocated_symbols); if(sorted_symbols != NULL) free(sorted_symbols); if(allocated_indirect_symbols != NULL) free(allocated_indirect_symbols); if(allocated_hints != NULL) free(allocated_hints); if(allocated_tocs != NULL) free(allocated_tocs); if(allocated_mods != NULL) free(allocated_mods); if(allocated_refs != NULL) free(allocated_refs); return result; } // derived from ofile.c ofile_process() in otool BOOL appContainsLibMatching(const char *appPath, const char *libs[]) { const char *name = appPath; struct arch_flag *arch_flags = NULL; unsigned long narch_flags = 0; enum bool all_archs = FALSE; enum bool process_non_objects = FALSE; // TRUE? enum bool dylib_flat = TRUE; char *member_name, *arch_name; unsigned long i; struct ofile ofile; enum bool flag, hostflag, arch_found, family; struct arch_flag host_arch_flag; const struct arch_flag *family_arch_flag; /* * If use_member_syntax is TRUE look for a name of the form * "archive(member)" which is to mean a member in that archive (the * member name must be at least one character long to be recognized as * this form). */ member_name = NULL; /* if(use_member_syntax == TRUE){ len = strlen(name); if(len >= 4 && name[len-1] == ')'){ p = strrchr(name, '('); if(p != NULL && p != name){ member_name = p+1; *p = '\0'; name[len-1] = '\0'; } } } */ #ifdef OTOOL otool_first_ofile_map = TRUE; #endif /* OTOOL */ if(ofile_map(name, NULL, NULL, &ofile, FALSE) == FALSE) return NO; #ifdef OTOOL otool_first_ofile_map = FALSE; #endif /* OTOOL */ if(ofile.file_type == OFILE_FAT){ /* * This is a fat file so see if a list of architecture is * specified and process only those. */ if(all_archs == FALSE && narch_flags != 0){ family = FALSE; if(narch_flags == 1){ family_arch_flag = get_arch_family_from_cputype(arch_flags[0].cputype); if(family_arch_flag != NULL) family = (enum bool)(family_arch_flag->cpusubtype == arch_flags[0].cpusubtype); } for(i = 0; i < narch_flags; i++){ (void)ofile_first_arch(&ofile); arch_found = FALSE; if(narch_flags != 1) arch_name = ofile.arch_flag.name; else arch_name = NULL; do{ if(ofile.arch_flag.cputype == arch_flags[i].cputype && (ofile.arch_flag.cpusubtype == arch_flags[i].cpusubtype || family == TRUE)){ arch_found = TRUE; if(ofile.arch_type == OFILE_ARCHIVE){ if(member_name != NULL){ if(ofile_specific_member(member_name, &ofile) == TRUE) processor(&ofile, arch_name, libs); } else{ /* loop through archive */ #ifdef OTOOL printf("Archive : %s", ofile.file_name); if(arch_name != NULL) printf(" (architecture %s)", arch_name); printf("\n"); #endif /* OTOOL */ if(ofile_first_member(&ofile) == TRUE){ flag = FALSE; do{ if(process_non_objects == TRUE || ofile.member_type == OFILE_Mach_O){ processor(&ofile, arch_name, libs); flag = TRUE; } }while(ofile_next_member(&ofile) == TRUE); if(flag == FALSE){ error("for architecture: %s " "archive: %s contains no " "members that are object " "files", ofile.arch_flag.name, ofile.file_name); } } else{ error("for architecture: %s archive: " "%s contains no members", ofile.arch_flag.name, ofile.file_name); } } } else if(process_non_objects == TRUE || ofile.arch_type == OFILE_Mach_O){ if(ofile.arch_type == OFILE_Mach_O && ofile.mh->filetype == MH_DYLIB){ if(dylib_flat == TRUE){ processor(&ofile, arch_name, libs); } else{ if(member_name != NULL){ if(ofile_specific_module( member_name, &ofile) == TRUE) processor(&ofile, arch_name, libs); } else{ /*loop through the dynamic library*/ if(ofile_first_module(&ofile)){ do{ processor(&ofile, arch_name, libs); }while(ofile_next_module( &ofile)); } else{ error("for architecture: %s " "dynamic library: %s " "contains no modules", ofile.arch_flag.name, ofile.file_name); } } } } else{ if(member_name != NULL) error("for architecture: %s file: %s " "is not an archive and thus does " "not contain member: %s", ofile.arch_flag.name, ofile.file_name, member_name); else processor(&ofile, arch_name, libs); } } else if(ofile.arch_type == OFILE_UNKNOWN){ error("for architecture: %s file: %s is " "not an object file", ofile.arch_flag.name,ofile.file_name); } break; } }while(ofile_next_arch(&ofile) == TRUE); if(arch_found == FALSE) error("file: %s does not contain architecture: %s", ofile.file_name, arch_flags[i].name); } return NO; } /* * This is a fat file and no architectures has been specified * so if it contains the host architecture process only that * architecture but if not process all architectures * specified. */ if(all_archs == FALSE){ (void)get_arch_from_host(&host_arch_flag, NULL); hostflag = FALSE; family = FALSE; family_arch_flag = get_arch_family_from_cputype(host_arch_flag.cputype); if(family_arch_flag != NULL) family = (enum bool)(family_arch_flag->cpusubtype == host_arch_flag.cpusubtype); ofile_unmap(&ofile); if(ofile_map(name, NULL, NULL, &ofile, FALSE) == FALSE) return NO; (void)ofile_first_arch(&ofile); do{ if(ofile.arch_flag.cputype == host_arch_flag.cputype && (ofile.arch_flag.cpusubtype == host_arch_flag.cpusubtype || family == TRUE)){ hostflag = TRUE; if(ofile.arch_type == OFILE_ARCHIVE){ if(member_name != NULL){ if(ofile_specific_member(member_name, &ofile) == TRUE) processor(&ofile, NULL, libs); } else{ /* loop through archive */ #ifdef OTOOL printf("Archive : %s\n", ofile.file_name); #endif /* OTOOL */ if(ofile_first_member(&ofile) == TRUE){ flag = FALSE; do{ if(process_non_objects == TRUE || ofile.member_type == OFILE_Mach_O){ processor(&ofile, NULL, libs); flag = TRUE; } }while(ofile_next_member(&ofile) == TRUE); if(flag == FALSE){ error("archive: %s contains no " "members that are object " "files", ofile.file_name); } } else{ error("archive: %s contains no " "members", ofile.file_name); } } } else if(process_non_objects == TRUE || ofile.arch_type == OFILE_Mach_O){ if(ofile.arch_type == OFILE_Mach_O && ofile.mh->filetype == MH_DYLIB){ if(dylib_flat == TRUE){ processor(&ofile, NULL, libs); } else{ if(member_name != NULL){ if(ofile_specific_module(member_name, &ofile) == TRUE) processor(&ofile, NULL, libs); } else{ /* loop through the dynamic library */ if(ofile_first_module(&ofile) == TRUE){ do{ processor(&ofile, NULL, libs); }while(ofile_next_module(&ofile)); } else{ error("for architecture: %s dynamic" " library: %s contains no " "modules", ofile.arch_flag.name, ofile.file_name); } } } } else{ if(member_name != NULL) error("for architecture: %s file: %s is " "not an archive and thus does not " "contain member: %s", ofile.arch_flag.name, ofile.file_name, member_name); else processor(&ofile, NULL, libs); } } else if(ofile.arch_type == OFILE_UNKNOWN){ error("file: %s is not an object file", ofile.file_name); } } }while(hostflag == FALSE && ofile_next_arch(&ofile) == TRUE); if(hostflag == TRUE) return NO; } /* * Either all architectures have been specified or none have * been specified and it does not contain the host architecture * so do all the architectures in the fat file */ ofile_unmap(&ofile); if(ofile_map(name, NULL, NULL, &ofile, FALSE) == FALSE) return NO; (void)ofile_first_arch(&ofile); do{ if(ofile.arch_type == OFILE_ARCHIVE){ if(member_name != NULL){ if(ofile_specific_member(member_name, &ofile) == TRUE) processor(&ofile, ofile.arch_flag.name, libs); } else{ /* loop through archive */ #ifdef OTOOL printf("Archive : %s (architecture %s)\n", ofile.file_name, ofile.arch_flag.name); #endif /* OTOOL */ if(ofile_first_member(&ofile) == TRUE){ flag = FALSE; do{ if(process_non_objects == TRUE || ofile.member_type == OFILE_Mach_O){ processor(&ofile, ofile.arch_flag.name, libs); flag = TRUE; } }while(ofile_next_member(&ofile) == TRUE); if(flag == FALSE){ error("for architecture: %s archive: %s " "contains no members that are object " "files", ofile.arch_flag.name, ofile.file_name); } } else{ error("for architecture: %s archive: %s " "contains no members", ofile.arch_flag.name, ofile.file_name); } } } else if(process_non_objects == TRUE || ofile.arch_type == OFILE_Mach_O){ if(ofile.arch_type == OFILE_Mach_O && ofile.mh->filetype == MH_DYLIB){ if(dylib_flat == TRUE){ processor(&ofile, ofile.arch_flag.name, libs); } else{ if(member_name != NULL){ if(ofile_specific_module(member_name, &ofile) == TRUE) processor(&ofile, ofile.arch_flag.name, libs); } else{ /* loop through the dynamic library */ if(ofile_first_module(&ofile) == TRUE){ do{ processor(&ofile, ofile.arch_flag.name, libs); }while(ofile_next_module(&ofile) == TRUE); } else{ error("for architecture: %s dynamic library" ": %s contains no modules", ofile.arch_flag.name,ofile.file_name); } } } } else{ if(member_name != NULL) error("for architecture: %s file: %s is not an " "archive and thus does not contain member: " "%s", ofile.arch_flag.name, ofile.file_name, member_name); else processor(&ofile, ofile.arch_flag.name, libs); } } else if(ofile.arch_type == OFILE_UNKNOWN){ error("for architecture: %s file: %s is not an " "object file", ofile.arch_flag.name, ofile.file_name); } }while(ofile_next_arch(&ofile) == TRUE); } else if(ofile.file_type == OFILE_ARCHIVE){ if(narch_flags != 0){ arch_found = FALSE; for(i = 0; i < narch_flags; i++){ family = FALSE; if(narch_flags == 1){ family_arch_flag = get_arch_family_from_cputype(arch_flags[0].cputype); if(family_arch_flag != NULL) family = (enum bool)(family_arch_flag->cpusubtype == arch_flags[0].cpusubtype); } if(ofile.archive_cputype == arch_flags[i].cputype && (ofile.archive_cpusubtype == arch_flags[i].cpusubtype || family == TRUE)){ arch_found = TRUE; } else{ error("file: %s does not contain architecture: %s", ofile.file_name, arch_flags[i].name); } } if(arch_found == FALSE) return NO; } if(member_name != NULL){ if(ofile_specific_member(member_name, &ofile) == TRUE) processor(&ofile, NULL, libs); } else{ /* loop through archive */ #ifdef OTOOL printf("Archive : %s\n", ofile.file_name); #endif /* OTOOL */ if(ofile_first_member(&ofile) == TRUE){ flag = FALSE; do{ if(process_non_objects == TRUE || ofile.member_type == OFILE_Mach_O){ processor(&ofile, NULL, libs); flag = TRUE; } }while(ofile_next_member(&ofile) == TRUE); if(flag == FALSE){ error("archive: %s contains no members that are " "object files", ofile.file_name); } } else{ error("archive: %s contains no members", ofile.file_name); } } } else if(ofile.file_type == OFILE_Mach_O){ if(narch_flags != 0){ arch_found = FALSE; for(i = 0; i < narch_flags; i++){ family = FALSE; if(narch_flags == 1){ family_arch_flag = get_arch_family_from_cputype(arch_flags[0].cputype); if(family_arch_flag != NULL) family = (enum bool)(family_arch_flag->cpusubtype == arch_flags[0].cpusubtype); } #ifdef OTOOL if(ofile.mh->magic == SWAP_LONG(MH_MAGIC)){ if(SWAP_LONG(ofile.mh->cputype) == arch_flags[i].cputype && (SWAP_LONG(ofile.mh->cpusubtype) == arch_flags[i].cpusubtype || family == TRUE)){ arch_found = TRUE; } } else #endif /* OTOOL */ if(ofile.mh->cputype == arch_flags[i].cputype && (ofile.mh->cpusubtype == arch_flags[i].cpusubtype || family == TRUE)){ arch_found = TRUE; } else{ error("file: %s does not contain architecture: %s", ofile.file_name, arch_flags[i].name); } } if(arch_found == FALSE) return NO; } if(ofile.mh->filetype == MH_DYLIB){ if(dylib_flat == TRUE){ processor(&ofile, NULL, libs); } else{ if(member_name != NULL){ if(ofile_specific_module(member_name, &ofile) == TRUE) processor(&ofile, NULL, libs); } else{ /* loop through the dynamic library */ if(ofile_first_module(&ofile) == TRUE){ do{ processor(&ofile, NULL, libs); }while(ofile_next_module(&ofile) == TRUE); } else{ error("dynamic library: %s contains no modules", ofile.file_name); } } } } else{ if(member_name != NULL) error("file: %s is not an archive and thus does not contain" " member: %s", ofile.file_name, member_name); else return processor(&ofile, NULL, libs); } } else{ if(process_non_objects == TRUE) processor(&ofile, NULL, libs); else if(member_name != NULL) error("file: %s(%s) is not an object file", name, member_name); else error("file: %s is not an object file", name); } return NO; } // derived from ofile_print.c print_libraries() in otool BOOL ofile_matches_lib(struct mach_header *mh, struct load_command *load_commands, enum byte_sex load_commands_byte_sex, enum bool just_id, const char **libs) { enum byte_sex host_byte_sex; enum bool swapped; unsigned long i, left, size; struct load_command *lc, l; struct fvmlib_command fl; struct dylib_command dl; char *p; host_byte_sex = get_host_byte_sex(); swapped = host_byte_sex != load_commands_byte_sex; lc = load_commands; for(i = 0 ; i < mh->ncmds; i++){ memcpy((char *)&l, (char *)lc, sizeof(struct load_command)); if(swapped) swap_load_command(&l, host_byte_sex); if(l.cmdsize % sizeof(long) != 0) printf("load command %lu size not a multiple of sizeof(long)\n", i); if((char *)lc + l.cmdsize > (char *)load_commands + mh->sizeofcmds) printf("load command %lu extends past end of load commands\n", i); left = mh->sizeofcmds - ((char *)lc - (char *)load_commands); switch(l.cmd){ case LC_IDFVMLIB: case LC_LOADFVMLIB: if(just_id == TRUE) break; memset((char *)&fl, '\0', sizeof(struct fvmlib_command)); size = left < sizeof(struct fvmlib_command) ? left : sizeof(struct fvmlib_command); memcpy((char *)&fl, (char *)lc, size); if(swapped) swap_fvmlib_command(&fl, host_byte_sex); if(fl.fvmlib.name.offset < fl.cmdsize){ p = (char *)lc + fl.fvmlib.name.offset; printf("\t%s (minor version %lu)\n", p, fl.fvmlib.minor_version); } else{ printf("\tBad offset (%lu) for name of %s command %lu\n", fl.fvmlib.name.offset, l.cmd == LC_IDFVMLIB ? "LC_IDFVMLIB" : "LC_LOADFVMLIB" , i); } break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: if(just_id == TRUE) break; case LC_ID_DYLIB: memset((char *)&dl, '\0', sizeof(struct dylib_command)); size = left < sizeof(struct dylib_command) ? left : sizeof(struct dylib_command); memcpy((char *)&dl, (char *)lc, size); if(swapped) swap_dylib_command(&dl, host_byte_sex); if(dl.dylib.name.offset < dl.cmdsize) { p = (char *)lc + dl.dylib.name.offset; if(just_id == TRUE) printf("%s\n", p); else printf("\t%s (compatibility version %lu.%lu.%lu, " "current version %lu.%lu.%lu)\n", p, dl.dylib.compatibility_version >> 16, (dl.dylib.compatibility_version >> 8) & 0xff, dl.dylib.compatibility_version & 0xff, dl.dylib.current_version >> 16, (dl.dylib.current_version >> 8) & 0xff, dl.dylib.current_version & 0xff); { const char **libName; for (libName = libs ; *libName != NULL ; libName++) { if (strstr(p, *libName) != NULL) return YES; } } } else{ printf("\tBad offset (%lu) for name of %s command %lu\n", dl.dylib.name.offset, l.cmd == LC_ID_DYLIB ? "LC_ID_DYLIB" : (l.cmd == LC_LOAD_DYLIB ? "LC_LOAD_DYLIB" : "LC_LOAD_WEAK_DYLIB") , i); } break; } if(l.cmdsize == 0){ printf("load command %lu size zero (can't advance to other " "load commands)\n", i); return NO; } lc = (struct load_command *)((char *)lc + l.cmdsize); if((char *)lc > (char *)load_commands + mh->sizeofcmds) return NO; } if((char *)load_commands + mh->sizeofcmds != (char *)lc) printf("Inconsistent mh_sizeofcmds\n"); return NO; }