blob: d5469d584ca64e102fa294a1cb4984f8cc9a628f [file] [log] [blame]
//
// $Id: class-dump.m,v 1.11.2.1 2003/09/05 21:25:55 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, 1998, 1999, 2000, 2001 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@omnigroup.com
//
#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 || defined(__APPLE__)
#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_SUB_FRAMEWORK
#define LC_SUB_FRAMEWORK 0x12
#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
//----------------------------------------------------------------------
// begin wolf
#define CLASS_DUMP_VERSION "2.1.5-wolf"
// end wolf
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;
BOOL generate_headers_flag = NO;
int swap_fat = 0;
int swap_macho = 0;
#define MAX_SECTIONS 2048
NSMutableArray *mappedFiles;
NSMutableDictionary *mappedFilesByInstallName;
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_CSTRING "__cstring" /* In SEG_TEXT segments */
#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",
"LC_ROUTINES",
"LC_SUB_FRAMEWORK",
};
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);
void *translate_address_to_pointer_complain (long addr, char *section, BOOL complain);
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 print_header (void);
//======================================================================
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: %08x\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 <= LC_SUB_FRAMEWORK)
{
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) ||
!strcmp (name, SEG_TEXT) || /* for MacOS X __cstring sections */
!strcmp (name, "") /* for .o files. */)
{
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;
if (!strcmp(section->segname, SEG_OBJC) ||
(!strcmp(section->segname, SEG_TEXT) &&
!strcmp(section->sectname, SEC_CSTRING)))
{
section_count++;
}
section++;
}
}
//----------------------------------------------------------------------
// 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)
{
return translate_address_to_pointer_complain (addr, section, YES);
}
void *translate_address_to_pointer_complain (long addr, char *section, BOOL complain)
{
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 && complain)
printf ("address (0x%08lx) not in '%s' section of OBJC segment!\n", addr, section);
return NULL;
}
//----------------------------------------------------------------------
char *string_at (long addr, char *section)
{
/* String addresses are located in a different section in MacOS X binaries.
Look there first, and only print error message if not found in either
the old or new style section. MacOS X still supports older Mac OS X
Server binaries, so we do need to look in both places.
*/
char *ptr = (char *)translate_address_to_pointer_complain (addr, SEC_CSTRING, NO);
if (ptr == NULL)
ptr = (char *)translate_address_to_pointer_complain (addr, section, YES);
return ptr;
}
//----------------------------------------------------------------------
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++)
{
// Sometimes the name, types, and implementation are all zero. However, the
// implementation may legitimately be zero (most often the first method of an object file),
// so we check the name instead.
if (method->name != 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;
// begin wolf
id en2, thing2;
NSMutableDictionary *categoryByName = [NSMutableDictionary dictionaryWithCapacity:5];
// end wolf
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;
// begin wolf
if (generate_headers_flag == YES)
flags |= F_SHOW_IMPORT;
// end wolf
tmp = current_filename;
m = module_info->start;
module_count = module_info->size / sizeof (struct my_objc_module);
{
MappedFile *currentFile;
NSString *installName, *filename;
currentFile = [mappedFilesByInstallName objectForKey:[NSString stringWithCString:module_info->filename]];
installName = [currentFile installName];
filename = [currentFile filename];
// begin wolf
if( generate_headers_flag == NO ) {
// end wolf
if (filename == nil || [installName isEqual:filename] == YES)
{
printf ("\n/*\n * File: %s\n */\n\n", module_info->filename);
}
else
{
printf ("\n/*\n * File: %s\n * Install name: %s\n */\n\n", [filename cString], module_info->filename);
}
// begin wolf
}
// end wolf
}
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++;
}
// begin wolf
en = [[protocols allKeys] objectEnumerator];
while( key = [en nextObject] ) {
thing = [protocols objectForKey:key];
int old_stdout = dup( 1 );
freopen( [[NSString stringWithFormat:@"%@.h", [thing protocolName]] cString], "w", stdout );
[thing showDefinition:flags];
fclose( stdout );
fdopen( old_stdout, "w" );
}
en = [classList objectEnumerator];
while( thing = [en nextObject] ) {
if( [thing isKindOfClass:[ObjcCategory class]] ) {
NSMutableArray *categoryArray = [categoryByName objectForKey:[thing categoryName]];
if( categoryArray ) {
[categoryArray addObject:thing];
} else {
[categoryByName setObject:[NSMutableArray arrayWithObject:thing] forKey:[thing categoryName]];
}
} else {
int old_stdout = dup( 1 );
freopen( [[NSString stringWithFormat:@"%@.h", [thing className]] cString], "w", stdout );
[thing showDefinition:flags];
fclose( stdout );
fdopen( old_stdout, "w" );
}
}
en = [[categoryByName allKeys] objectEnumerator];
while( key = [en nextObject] ) {
int old_stdout = dup( 1 );
freopen( [[NSString stringWithFormat:@"%@.h", key] cString], "w", stdout );
print_header();
thing = [categoryByName objectForKey:key];
en2 = [thing objectEnumerator];
while( thing2 = [en2 nextObject] ) {
[thing2 showDefinition:flags];
}
fclose( stdout );
fdopen( old_stdout, "w" );
}
// end wolf
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 installName] cString]))
return;
}
mappedFile = [[[MappedFile alloc] initWithFilename:[NSString stringWithCString:filename]] autorelease];
if (mappedFile != nil)
{
[mappedFiles addObject:mappedFile];
[mappedFilesByInstallName setObject:mappedFile forKey:[mappedFile installName]];
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"
// begin wolf
" -S sort protocols, classes, and methods\n"
" -H generate header files in current directory\n",
// end wolf
CLASS_DUMP_VERSION
);
}
//----------------------------------------------------------------------
void print_header (void)
{
printf (
"/*\n"
" * Generated by class-dump (version %s).\n"
" *\n"
" * class-dump is Copyright (C) 1997, 1999, 2000, 2001 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);
}
// begin wolf
while ( (c = getopt (argc, argv, "aAeRC:rsSH")) != EOF)
// end wolf
{
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;
// begin wolf
case 'H':
generate_headers_flag = YES;
break;
// end wolf
case '?':
default:
error_flag++;
break;
}
}
if (error_flag > 0)
{
print_usage ();
exit (2);
}
mappedFiles = [NSMutableArray array];
mappedFilesByInstallName = [NSMutableDictionary dictionary];
protocols = [NSMutableDictionary dictionary];
if (optind < argc)
{
build_up_objc_segments (argv[optind]);
// begin wolf
if( generate_headers_flag == NO )
print_header ();
// end wolf
//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;
}