This is class-dump.m in view mode; [Download] [Up]
// // $Id: class-dump.m,v 1.9 1997/12/08 08:03:00 nygard Exp $ // // // This file is a part of class-dump v2, a utility for examining the // Objective-C segment of Mach-O files. // Copyright (C) 1997 Steve Nygard // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // You may contact the author by: // e-mail: nygard@telusplanet.net // #include <stdio.h> #include <libc.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <mach/mach.h> #include <mach/mach_error.h> #include <mach-o/loader.h> #include <mach-o/fat.h> #if NS_TARGET_MAJOR >= 4 #import <Foundation/Foundation.h> #else #import <foundation/NSString.h> #import <foundation/NSArray.h> #import <foundation/NSDictionary.h> #import <foundation/NSAutoreleasePool.h> #import <foundation/NSUtilities.h> #endif #include "datatypes.h" #include "class-dump.h" #include "my_regex.h" #import "ObjcThing.h" #import "ObjcClass.h" #import "ObjcCategory.h" #import "ObjcProtocol.h" #import "ObjcIvar.h" #import "ObjcMethod.h" #import "MappedFile.h" #ifndef LC_PREBOUND_DYLIB #define LC_PREBOUND_DYLIB 0x10 #endif #ifndef LC_LOAD_DYLIB #define LC_LOAD_DYLIB 0x0c struct dylib { union lc_str name; unsigned long timestamp; unsigned long current_version; unsigned long compatibility_version; }; struct dylib_command { unsigned long cmd; unsigned long cmdsize; struct dylib dylib; }; #endif //---------------------------------------------------------------------- #define CLASS_DUMP_VERSION "2.1.1" int expand_structures_flag = 0; int char_star_flag = 0; BOOL show_ivar_offsets_flag = NO; BOOL show_method_addresses_flag = NO; BOOL expand_protocols_flag = NO; BOOL match_flag = NO; BOOL expand_frameworks_flag = NO; BOOL sort_flag = NO; int swap_fat = 0; int swap_macho = 0; #define MAX_SECTIONS 2048 NSMutableArray *mappedFiles; char *current_filename = NULL; struct section_info { char *filename; char name[17]; struct section *section; void *start; long vmaddr; long size; } objc_sections[MAX_SECTIONS]; int section_count = 0; //---------------------------------------------------------------------- #define SEC_CLASS "__class" #define SEC_SYMBOLS "__symbols" #define SEC_PROTOCOL "__protocol" #define SEC_CATEGORY "__category" #define SEC_CLS_METH "__cls_meth" #define SEC_INST_METH "__inst_meth" #define SEC_META_CLASS "__meta_class" #define SEC_CLASS_NAMES "__class_names" #define SEC_MODULE_INFO "__module_info" #define SEC_CAT_CLS_METH "__cat_cls_meth" #define SEC_INSTANCE_VARS "__instance_vars" #define SEC_CAT_INST_METH "__cat_inst_meth" #define SEC_METH_VAR_TYPES "__meth_var_types" #define SEC_METH_VAR_NAMES "__meth_var_names" //====================================================================== char *file_type_names[] = { "MH_<unknown>", "MH_OBJECT", "MH_EXECUTE", "MH_FVMLIB", "MH_CORE", "MH_PRELOAD", "MH_DYLIB", "MH_DYLINKER", "MH_BUNDLE", }; char *load_command_names[] = { "LC_<unknown>", "LC_SEGMENT", "LC_SYMTAB", "LC_SYMSEG", "LC_THREAD", "LC_UNIXTHREAD", "LC_LOADFVMLIB", "LC_IDFVMLIB", "LC_IDENT", "LC_FVMFILE", "LC_PREPAGE", "LC_DYSYMTAB", "LC_LOAD_DYLIB", "LC_ID_DYLIB", "LC_LOAD_DYLINKER", "LC_ID_DYLINKER", "LC_PREBOUND_DYLIB", }; NSMutableDictionary *protocols; //====================================================================== void process_file (void *ptr, char *filename); int process_macho (void *ptr, char *filename); unsigned long process_load_command (void *start, void *ptr, char *filename); void process_dylib_command (void *start, void *ptr); void process_fvmlib_command (void *start, void *ptr); void process_segment_command (void *start, void *ptr, char *filename); void process_objc_segment (void *start, void *ptr, char *filename); struct section_info *find_objc_section (char *name, char *filename); void *translate_address_to_pointer (long addr, char *section); char *string_at (long addr, char *section); NSString *nsstring_at (long addr, char *section); struct section_info *section_of_address (long addr); NSArray *handle_objc_symtab (struct my_objc_symtab *symtab); ObjcClass *handle_objc_class (struct my_objc_class *ocl); ObjcCategory *handle_objc_category (struct my_objc_category *ocat); NSArray *handle_objc_protocols (struct my_objc_protocol_list *plist, BOOL expandProtocols); NSArray *handle_objc_meta_class (struct my_objc_class *ocl); NSArray *handle_objc_ivars (struct my_objc_ivars *ivars); NSArray *handle_objc_methods (struct my_objc_methods *methods, char ch); void show_single_module (struct section_info *module_info); void show_all_modules (void); void build_up_objc_segments (char *filename); //====================================================================== void process_file (void *ptr, char *filename) { struct mach_header *mh = (struct mach_header *)ptr; struct fat_header *fh = (struct fat_header *)ptr; struct fat_arch *fa = (struct fat_arch *)(fh + 1); int l; int result = 1; if (mh->magic == FAT_CIGAM) { // Fat file... Other endian. swap_fat = 1; for (l = 0; l < NXSwapLong (fh->nfat_arch); l++) { #ifdef VERBOSE printf ("archs: %ld\n", NXSwapLong (fh->nfat_arch)); printf ("offset: %lx\n", NXSwapLong (fa->offset)); printf ("arch: %08lx\n", NXSwapLong (fa->cputype)); #endif result = process_macho (ptr + NXSwapLong (fa->offset), filename); if (result == 0) break; fa++; } } else if (mh->magic == FAT_MAGIC) { // Fat file... This endian. for (l = 0; l < fh->nfat_arch; l++) { #ifdef VERBOSE printf ("archs: %ld\n", fh->nfat_arch); printf ("offset: %lx\n", fa->offset); printf ("arch: %08lx\n", fa->cputype); #endif result = process_macho (ptr + fa->offset, filename); if (result == 0) break; fa++; } } else { result = process_macho (ptr, filename); } switch (result) { case 0: break; case 1: printf ("Error: File did not contain an executable with our endian.\n"); break; default: printf ("Error: processing Mach-O file.\n"); } } //---------------------------------------------------------------------- // Returns 0 if this was our endian, 1 if it was not, 2 otherwise. int process_macho (void *ptr, char *filename) { struct mach_header *mh = (struct mach_header *)ptr; int l; void *start = ptr; if (mh->magic == MH_CIGAM) { swap_macho = 1; return 1; } else if (mh->magic != MH_MAGIC) { printf ("This is not a Mach-O file.\n"); return 2; } ptr += sizeof (struct mach_header); for (l = 0; l < mh->ncmds; l++) { ptr += process_load_command (start, ptr, filename); } return 0; } //---------------------------------------------------------------------- unsigned long process_load_command (void *start, void *ptr, char *filename) { struct load_command *lc = (struct load_command *)ptr; #ifdef VERBOSE if (lc->cmd >= 0 && lc->cmd <= LC_PREBOUND_DYLIB) { printf ("%s\n", load_command_names[ lc->cmd ]); } else { printf ("%08lx\n", lc->cmd); } #endif if (lc->cmd == LC_SEGMENT) { process_segment_command (start, ptr, filename); } else if (lc->cmd == LC_LOAD_DYLIB) { process_dylib_command (start, ptr); } else if (lc->cmd == LC_LOADFVMLIB) { process_fvmlib_command (start, ptr); } return lc->cmdsize; } //---------------------------------------------------------------------- void process_dylib_command (void *start, void *ptr) { struct dylib_command *dc = (struct dylib_command *)ptr; build_up_objc_segments (ptr + dc->dylib.name.offset); } //---------------------------------------------------------------------- void process_fvmlib_command (void *start, void *ptr) { struct fvmlib_command *fc = (struct fvmlib_command *)ptr; build_up_objc_segments (ptr + fc->fvmlib.name.offset); } //---------------------------------------------------------------------- void process_segment_command (void *start, void *ptr, char *filename) { struct segment_command *sc = (struct segment_command *)ptr; char name[17]; strncpy (name, sc->segname, 16); name[16] = 0; if (!strcmp (name, SEG_OBJC)) { process_objc_segment (start, ptr, filename); } } //---------------------------------------------------------------------- void process_objc_segment (void *start, void *ptr, char *filename) { struct segment_command *sc = (struct segment_command *)ptr; struct section *section = (struct section *)(sc + 1); int l; for (l = 0; l < sc->nsects; l++) { if (section_count >= MAX_SECTIONS) { printf ("Error: Maximum number of sections reached.\n"); return; } objc_sections[section_count].filename = filename; strncpy (objc_sections[section_count].name, section->sectname, 16); objc_sections[section_count].name[16] = 0; objc_sections[section_count].section = section; objc_sections[section_count].start = start + section->offset; objc_sections[section_count].vmaddr = section->addr; objc_sections[section_count].size = section->size; section++; section_count++; } } //---------------------------------------------------------------------- // Find the Objective-C segment for the given filename noted in our // list. struct section_info *find_objc_section (char *name, char *filename) { int l; for (l = 0; l < section_count; l++) { if (!strcmp (name, objc_sections[l].name) && !strcmp (filename, objc_sections[l].filename)) { return &objc_sections[l]; } } return NULL; } //---------------------------------------------------------------------- void debug_section_overlap (void) { int l; for (l = 0; l < section_count; l++) { printf ("%10ld to %10ld [size 0x%08ld] %-16s of %s\n", objc_sections[l].vmaddr, objc_sections[l].vmaddr + objc_sections[l].size, objc_sections[l].size, objc_sections[l].name, objc_sections[l].filename); } } //---------------------------------------------------------------------- // // Take a long from the Mach-O file (which is really a pointer when // the section is loaded at the proper location) and translate it into // a pointer to where we have the file mapped. // void *translate_address_to_pointer (long addr, char *section) { int l; int count = 0; for (l = 0; l < section_count; l++) { if (addr >= objc_sections[l].vmaddr && addr < objc_sections[l].vmaddr + objc_sections[l].size && !strcmp (objc_sections[l].name, section)) { count++; } } if (count > 1) { // If there are still duplicates, we choose the one for the current file. for (l = 0; l < section_count; l++) { if (addr >= objc_sections[l].vmaddr && addr < objc_sections[l].vmaddr + objc_sections[l].size && !strcmp (objc_sections[l].name, section) && !strcmp (objc_sections[l].filename, current_filename)) { return objc_sections[l].start + addr - objc_sections[l].vmaddr; } } } else { for (l = 0; l < section_count; l++) { if (addr >= objc_sections[l].vmaddr && addr < objc_sections[l].vmaddr + objc_sections[l].size && !strcmp (objc_sections[l].name, section)) { return objc_sections[l].start + addr - objc_sections[l].vmaddr; } } } if (addr != 0) printf ("address (0x%08lx) not in OBJC segment!\n", addr); return NULL; } //---------------------------------------------------------------------- char *string_at (long addr, char *section) { return (char *)translate_address_to_pointer (addr, section); } //---------------------------------------------------------------------- NSString *nsstring_at (long addr, char *section) { char *str = string_at (addr, section); return (str == NULL) ? (NSString *)@"" : [NSString stringWithCString:str]; } //---------------------------------------------------------------------- struct section_info *section_of_address (long addr) { int l; for (l = 0; l < section_count; l++) { if (addr >= objc_sections[l].vmaddr && addr < objc_sections[l].vmaddr + objc_sections[l].size) { return &objc_sections[l]; } } return NULL; } //====================================================================== NSArray *handle_objc_symtab (struct my_objc_symtab *symtab) { NSMutableArray *classList = [NSMutableArray array]; ObjcThing *objcThing; long *class_pointer; int l; if (symtab == NULL) { printf ("NULL symtab...\n"); return nil; } class_pointer = &symtab->class_pointer; for (l = 0; l < symtab->cls_def_count; l++) { objcThing = handle_objc_class (translate_address_to_pointer (*class_pointer, SEC_CLASS)); if (objcThing != nil) [classList addObject:objcThing]; class_pointer++; } for (l = 0; l < symtab->cat_def_count; l++) { objcThing = handle_objc_category (translate_address_to_pointer (*class_pointer, SEC_CATEGORY)); if (objcThing != nil) [classList addObject:objcThing]; class_pointer++; } return classList; } //---------------------------------------------------------------------- ObjcClass *handle_objc_class (struct my_objc_class *ocl) { ObjcClass *objcClass; NSArray *tmp; if (ocl == NULL) return nil; tmp = handle_objc_protocols ((struct my_objc_protocol_list *)translate_address_to_pointer (ocl->protocols, SEC_CAT_CLS_METH), YES); if (string_at (ocl->super_class, SEC_CLASS_NAMES) == NULL) { objcClass = [[[ObjcClass alloc] initWithClassName:nsstring_at (ocl->name, SEC_CLASS_NAMES) superClassName:nil] autorelease]; } else { objcClass = [[[ObjcClass alloc] initWithClassName:nsstring_at (ocl->name, SEC_CLASS_NAMES) superClassName:nsstring_at (ocl->super_class, SEC_CLASS_NAMES)] autorelease]; } [objcClass addProtocolNames:tmp]; tmp = handle_objc_ivars ((struct my_objc_ivars *)translate_address_to_pointer (ocl->ivars, SEC_INSTANCE_VARS)); [objcClass addIvars:tmp]; tmp = handle_objc_meta_class ((struct my_objc_class *)translate_address_to_pointer (ocl->isa, SEC_META_CLASS)); [objcClass addClassMethods:tmp]; tmp = handle_objc_methods ((struct my_objc_methods *)translate_address_to_pointer (ocl->methods, SEC_INST_METH), '-'); [objcClass addInstanceMethods:tmp]; return objcClass; } //---------------------------------------------------------------------- ObjcCategory *handle_objc_category (struct my_objc_category *ocat) { ObjcCategory *objcCategory; NSArray *tmp; if (ocat == NULL) return nil; objcCategory = [[[ObjcCategory alloc] initWithClassName:nsstring_at (ocat->class_name, SEC_CLASS_NAMES) categoryName:nsstring_at (ocat->category_name, SEC_CLASS_NAMES)] autorelease]; tmp = handle_objc_methods ((struct my_objc_methods *)translate_address_to_pointer (ocat->class_methods, SEC_CAT_CLS_METH), '+'); [objcCategory addClassMethods:tmp]; tmp = handle_objc_methods ((struct my_objc_methods *)translate_address_to_pointer (ocat->methods, SEC_CAT_INST_METH), '-'); [objcCategory addInstanceMethods:tmp]; return objcCategory; } //---------------------------------------------------------------------- // Return list of protocol names. NSArray *handle_objc_protocols (struct my_objc_protocol_list *plist, BOOL expandProtocols) { NSMutableArray *protocolArray = [NSMutableArray array]; ObjcProtocol *objcProtocol; struct my_objc_protocol *prot; struct my_objc_prot_inst_meth_list *mlist; struct my_objc_prot_inst_meth *meth; int l, p; long *ptr; NSArray *parentProtocols; if (plist == NULL) return nil; ptr = &plist->list; for (p = 0; p < plist->count; p++) { prot = translate_address_to_pointer (*ptr, SEC_PROTOCOL); objcProtocol = [[[ObjcProtocol alloc] initWithProtocolName:nsstring_at (prot->protocol_name, SEC_CLASS_NAMES)] autorelease]; [protocolArray addObject:[objcProtocol protocolName]]; parentProtocols = handle_objc_protocols (translate_address_to_pointer (prot->protocol_list, SEC_CAT_CLS_METH), expand_protocols_flag); [objcProtocol addProtocolNames:parentProtocols]; mlist = translate_address_to_pointer (prot->instance_methods, SEC_CAT_INST_METH); if (mlist != NULL) { meth = (struct my_objc_prot_inst_meth *)&mlist->methods; for (l = 0; l < mlist->count; l++) { [objcProtocol addProtocolMethod:[[[ObjcMethod alloc] initWithMethodName:nsstring_at (meth->name, SEC_METH_VAR_NAMES) type:nsstring_at (meth->types, SEC_METH_VAR_TYPES)] autorelease]]; meth++; } } if (expandProtocols == YES && [protocols objectForKey:[objcProtocol protocolName]] == nil) { [protocols setObject:objcProtocol forKey:[objcProtocol protocolName]]; objcProtocol = nil; } ptr++; } return protocolArray; } //---------------------------------------------------------------------- NSArray *handle_objc_meta_class (struct my_objc_class *ocl) { if (ocl == NULL) return nil; return handle_objc_methods ((struct my_objc_methods *)translate_address_to_pointer (ocl->methods, SEC_CLS_METH), '+'); } //---------------------------------------------------------------------- NSArray *handle_objc_ivars (struct my_objc_ivars *ivars) { struct my_objc_ivar *ivar = (struct my_objc_ivar *)(ivars + 1); NSMutableArray *ivarArray = [NSMutableArray array]; ObjcIvar *objcIvar; int l; if (ivars == NULL) return nil; for (l = 0; l < ivars->ivar_count; l++) { objcIvar = [[[ObjcIvar alloc] initWithName:nsstring_at (ivar->name, SEC_METH_VAR_NAMES) type:nsstring_at (ivar->type, SEC_METH_VAR_TYPES) offset:ivar->offset] autorelease]; [ivarArray addObject:objcIvar]; ivar++; } return ivarArray; } //---------------------------------------------------------------------- NSArray *handle_objc_methods (struct my_objc_methods *methods, char ch) { struct my_objc_method *method = (struct my_objc_method *)(methods + 1); NSMutableArray *methodArray = [NSMutableArray array]; ObjcMethod *objcMethod; int l; if (methods == NULL) return nil; for (l = 0; l < methods->method_count; l++) { if (method->imp != 0) { objcMethod = [[[ObjcMethod alloc] initWithMethodName:nsstring_at (method->name, SEC_METH_VAR_NAMES) type:nsstring_at (method->types, SEC_METH_VAR_TYPES) address:method->imp] autorelease]; [methodArray addObject:objcMethod]; } method++; } return methodArray; } //====================================================================== void show_single_module (struct section_info *module_info) { struct my_objc_module *m; int module_count; int l; char *tmp; id en, thing, key; NSMutableArray *classList = [NSMutableArray array]; NSArray *newClasses; int flags = 0; if (module_info == NULL) { return; } if (sort_flag == YES) flags |= F_SORT_METHODS; if (show_ivar_offsets_flag == YES) flags |= F_SHOW_IVAR_OFFSET; if (show_method_addresses_flag == YES) flags |= F_SHOW_METHOD_ADDRESS; tmp = current_filename; m = module_info->start; module_count = module_info->size / sizeof (struct my_objc_module); printf ("\n/*\n * File: %s\n */\n\n", module_info->filename); current_filename = module_info->filename; for (l = 0; l < module_count; l++) { newClasses = handle_objc_symtab ((struct my_objc_symtab *)translate_address_to_pointer (m->symtab, SEC_SYMBOLS)); [classList addObjectsFromArray:newClasses]; m++; } if (sort_flag == YES) en = [[[protocols allKeys] sortedArrayUsingSelector:@selector (compare:)] objectEnumerator]; else en = [[protocols allKeys] objectEnumerator]; while (key = [en nextObject]) { thing = [protocols objectForKey:key]; if (match_flag == NO || RE_EXEC ([[thing sortableName] cString]) == 1) [thing showDefinition:flags]; } if (sort_flag == YES) en = [[classList sortedArrayUsingSelector:@selector (orderByName:)] objectEnumerator]; else en = [classList objectEnumerator]; while (thing = [en nextObject]) { if (match_flag == NO || RE_EXEC ([[thing sortableName] cString]) == 1) [thing showDefinition:flags]; } [protocols removeAllObjects]; current_filename = tmp; } //---------------------------------------------------------------------- void show_all_modules (void) { int l; for (l = section_count - 1; l >= 0; l--) { if (!strcmp (objc_sections[l].name, SEC_MODULE_INFO)) { show_single_module ((struct section_info *)&objc_sections[l]); } } } //---------------------------------------------------------------------- void build_up_objc_segments (char *filename) { MappedFile *mappedFile; NSEnumerator *mfEnumerator; // Only process each file once. mfEnumerator = [mappedFiles objectEnumerator]; while (mappedFile = [mfEnumerator nextObject]) { if (!strcmp (filename, [[mappedFile filename] cString])) return; } mappedFile = [[[MappedFile alloc] initWithFilename:[NSString stringWithCString:filename]] autorelease]; //NSCAssert (mappedFile != nil, @"Could not map file..."); if (mappedFile != nil) { [mappedFiles addObject:mappedFile]; process_file ((void *)[mappedFile data], filename); } } //---------------------------------------------------------------------- void print_usage (void) { fprintf (stderr, "class-dump %s\n" "Usage: class-dump [-a] [-A] [-e] [-R] [-C regex] [-r] [-s] [-S] executable-file\n" " -a show instance variable offsets\n" " -A show implementation addresses\n" " -e expand structure (and union) definition whenever possible\n" " -R recursively expand @protocol <>\n" " -C only display classes matching regular expression\n" " -r recursively expand frameworks and fixed VM shared libraries\n" " -s convert STR to char *\n" " -S sort protocols, classes, and methods\n", CLASS_DUMP_VERSION ); } //---------------------------------------------------------------------- void print_header (void) { printf ( "/*\n" " * Generated by class-dump (version %s).\n" " *\n" " * class-dump is Copyright (C) 1997 by Steve Nygard.\n" " */\n", CLASS_DUMP_VERSION ); } //====================================================================== int main (int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int c; extern int optind; extern char *optarg; int error_flag = 0; const char *tmp; if (argc == 1) { print_usage(); exit (2); } while ( (c = getopt (argc, argv, "aAeRC:rsS")) != EOF) { switch (c) { case 'a': show_ivar_offsets_flag = YES; break; case 'A': show_method_addresses_flag = YES; break; case 'e': expand_structures_flag = 1; break; case 'R': expand_protocols_flag = YES; break; case 'C': if (match_flag == YES) { printf ("Error: sorry, only one -C allowed\n"); error_flag++; } else { match_flag = YES; tmp = RE_COMP (optarg); if (tmp != NULL) { printf ("Error: %s\n", tmp); exit (1); } } break; case 'r': expand_frameworks_flag = YES; break; case 's': char_star_flag = 1; break; case 'S': sort_flag = YES; break; case '?': default: error_flag++; break; } } if (error_flag > 0) { print_usage (); exit (2); } mappedFiles = [NSMutableArray array]; protocols = [NSMutableDictionary dictionary]; if (optind < argc) { build_up_objc_segments (argv[optind]); print_header (); //debug_section_overlap (); if (section_count > 0) { if (expand_frameworks_flag == NO) show_single_module ((struct section_info *)find_objc_section (SEC_MODULE_INFO, argv[optind])); else show_all_modules (); } } [mappedFiles removeAllObjects]; [pool release]; return 0; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.