| Index: class-dump/src/CDClassDump.m
|
| ===================================================================
|
| --- class-dump/src/CDClassDump.m (revision 0)
|
| +++ class-dump/src/CDClassDump.m (revision 0)
|
| @@ -0,0 +1,400 @@
|
| +// -*- mode: ObjC -*-
|
| +
|
| +// This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
|
| +// Copyright (C) 1997-1998, 2000-2001, 2004-2010 Steve Nygard.
|
| +
|
| +#import "CDClassDump.h"
|
| +
|
| +#import "NSArray-Extensions.h"
|
| +#import "NSString-Extensions.h"
|
| +#import "CDFatArch.h"
|
| +#import "CDFatFile.h"
|
| +#import "CDLCDylib.h"
|
| +#import "CDMachOFile.h"
|
| +#import "CDObjectiveCProcessor.h"
|
| +#import "CDStructureTable.h"
|
| +#import "CDSymbolReferences.h"
|
| +#import "CDType.h"
|
| +#import "CDTypeFormatter.h"
|
| +#import "CDTypeParser.h"
|
| +#import "CDVisitor.h"
|
| +#import "CDLCSegment.h"
|
| +#import "CDTypeController.h"
|
| +#import "CDSearchPathState.h"
|
| +
|
| +@implementation CDClassDump
|
| +
|
| +- (id)init;
|
| +{
|
| + if ([super init] == nil)
|
| + return nil;
|
| +
|
| + searchPathState = [[CDSearchPathState alloc] init];
|
| + sdkRoot = nil;
|
| +
|
| + machOFiles = [[NSMutableArray alloc] init];
|
| + machOFilesByID = [[NSMutableDictionary alloc] init];
|
| + objcProcessors = [[NSMutableArray alloc] init];
|
| +
|
| + typeController = [[CDTypeController alloc] initWithClassDump:self];
|
| +
|
| + // These can be ppc, ppc7400, ppc64, i386, x86_64
|
| + targetArch.cputype = CPU_TYPE_ANY;
|
| + targetArch.cpusubtype = 0;
|
| +
|
| + flags.shouldShowHeader = YES;
|
| +
|
| + return self;
|
| +}
|
| +
|
| +- (void)dealloc;
|
| +{
|
| + [searchPathState release];
|
| + [sdkRoot release];
|
| +
|
| + [machOFiles release];
|
| + [machOFilesByID release];
|
| + [objcProcessors release];
|
| +
|
| + [typeController release];
|
| +
|
| + if (flags.shouldMatchRegex)
|
| + regfree(&compiledRegex);
|
| +
|
| + [super dealloc];
|
| +}
|
| +
|
| +@synthesize searchPathState;
|
| +
|
| +- (BOOL)shouldProcessRecursively;
|
| +{
|
| + return flags.shouldProcessRecursively;
|
| +}
|
| +
|
| +- (void)setShouldProcessRecursively:(BOOL)newFlag;
|
| +{
|
| + flags.shouldProcessRecursively = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldSortClasses;
|
| +{
|
| + return flags.shouldSortClasses;
|
| +}
|
| +
|
| +- (void)setShouldSortClasses:(BOOL)newFlag;
|
| +{
|
| + flags.shouldSortClasses = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldSortClassesByInheritance;
|
| +{
|
| + return flags.shouldSortClassesByInheritance;
|
| +}
|
| +
|
| +- (void)setShouldSortClassesByInheritance:(BOOL)newFlag;
|
| +{
|
| + flags.shouldSortClassesByInheritance = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldSortMethods;
|
| +{
|
| + return flags.shouldSortMethods;
|
| +}
|
| +
|
| +- (void)setShouldSortMethods:(BOOL)newFlag;
|
| +{
|
| + flags.shouldSortMethods = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldShowIvarOffsets;
|
| +{
|
| + return flags.shouldShowIvarOffsets;
|
| +}
|
| +
|
| +- (void)setShouldShowIvarOffsets:(BOOL)newFlag;
|
| +{
|
| + flags.shouldShowIvarOffsets = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldShowMethodAddresses;
|
| +{
|
| + return flags.shouldShowMethodAddresses;
|
| +}
|
| +
|
| +- (void)setShouldShowMethodAddresses:(BOOL)newFlag;
|
| +{
|
| + flags.shouldShowMethodAddresses = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldMatchRegex;
|
| +{
|
| + return flags.shouldMatchRegex;
|
| +}
|
| +
|
| +- (void)setShouldMatchRegex:(BOOL)newFlag;
|
| +{
|
| + if (flags.shouldMatchRegex && newFlag == NO)
|
| + regfree(&compiledRegex);
|
| +
|
| + flags.shouldMatchRegex = newFlag;
|
| +}
|
| +
|
| +- (BOOL)shouldShowHeader;
|
| +{
|
| + return flags.shouldShowHeader;
|
| +}
|
| +
|
| +- (void)setShouldShowHeader:(BOOL)newFlag;
|
| +{
|
| + flags.shouldShowHeader = newFlag;
|
| +}
|
| +
|
| +- (BOOL)setRegex:(char *)regexCString errorMessage:(NSString **)errorMessagePointer;
|
| +{
|
| + int result;
|
| +
|
| + if (flags.shouldMatchRegex)
|
| + regfree(&compiledRegex);
|
| +
|
| + result = regcomp(&compiledRegex, regexCString, REG_EXTENDED);
|
| + if (result != 0) {
|
| + char regex_error_buffer[256];
|
| +
|
| + if (regerror(result, &compiledRegex, regex_error_buffer, 256) > 0) {
|
| + if (errorMessagePointer != NULL) {
|
| + *errorMessagePointer = [NSString stringWithUTF8String:regex_error_buffer];
|
| + }
|
| + } else {
|
| + if (errorMessagePointer != NULL)
|
| + *errorMessagePointer = nil;
|
| + }
|
| +
|
| + return NO;
|
| + }
|
| +
|
| + [self setShouldMatchRegex:YES];
|
| +
|
| + return YES;
|
| +}
|
| +
|
| +- (BOOL)regexMatchesString:(NSString *)aString;
|
| +{
|
| + int result;
|
| +
|
| + result = regexec(&compiledRegex, [aString UTF8String], 0, NULL, 0);
|
| + if (result != 0) {
|
| + if (result != REG_NOMATCH) {
|
| + char regex_error_buffer[256];
|
| +
|
| + if (regerror(result, &compiledRegex, regex_error_buffer, 256) > 0)
|
| + NSLog(@"Error with regex matching string, %@", [NSString stringWithUTF8String:regex_error_buffer]);
|
| + }
|
| +
|
| + return NO;
|
| + }
|
| +
|
| + return YES;
|
| +}
|
| +
|
| +@synthesize sdkRoot;
|
| +
|
| +- (NSArray *)machOFiles;
|
| +{
|
| + return machOFiles;
|
| +}
|
| +
|
| +- (NSArray *)objcProcessors;
|
| +{
|
| + return objcProcessors;
|
| +}
|
| +
|
| +@synthesize targetArch;
|
| +
|
| +- (BOOL)containsObjectiveCData;
|
| +{
|
| + for (CDObjectiveCProcessor *processor in objcProcessors) {
|
| + if ([processor hasObjectiveCData])
|
| + return YES;
|
| + }
|
| +
|
| + return NO;
|
| +}
|
| +
|
| +- (BOOL)hasEncryptedFiles;
|
| +{
|
| + for (CDMachOFile *machOFile in machOFiles) {
|
| + if ([machOFile isEncrypted]) {
|
| + return YES;
|
| + }
|
| + }
|
| +
|
| + return NO;
|
| +}
|
| +
|
| +- (CDTypeController *)typeController;
|
| +{
|
| + return typeController;
|
| +}
|
| +
|
| +- (BOOL)loadFile:(CDFile *)aFile;
|
| +{
|
| + CDMachOFile *aMachOFile;
|
| +
|
| + //NSLog(@"targetArch: (%08x, %08x)", targetArch.cputype, targetArch.cpusubtype);
|
| + aMachOFile = [aFile machOFileWithArch:targetArch];
|
| + //NSLog(@"aMachOFile: %@", aMachOFile);
|
| + if (aMachOFile == nil) {
|
| + fprintf(stderr, "Error: file doesn't contain the specified arch.\n\n");
|
| + return NO;
|
| + }
|
| +
|
| + // Set before processing recursively. This was getting caught on CoreUI on 10.6
|
| + assert([aMachOFile filename] != nil);
|
| + [machOFiles addObject:aMachOFile];
|
| + [machOFilesByID setObject:aMachOFile forKey:[aMachOFile filename]];
|
| +
|
| + if ([self shouldProcessRecursively]) {
|
| + @try {
|
| + for (CDLoadCommand *loadCommand in [aMachOFile loadCommands]) {
|
| + if ([loadCommand isKindOfClass:[CDLCDylib class]]) {
|
| + CDLCDylib *aDylibCommand;
|
| +
|
| + aDylibCommand = (CDLCDylib *)loadCommand;
|
| + if ([aDylibCommand cmd] == LC_LOAD_DYLIB) {
|
| + [searchPathState pushSearchPaths:[aMachOFile runPaths]];
|
| + [self machOFileWithID:[aDylibCommand path]]; // Loads as a side effect
|
| + [searchPathState popSearchPaths];
|
| + }
|
| + }
|
| + }
|
| + }
|
| + @catch (NSException *exception) {
|
| + [aMachOFile release];
|
| + return NO;
|
| + }
|
| + }
|
| +
|
| + return YES;
|
| +}
|
| +
|
| +- (void)processObjectiveCData;
|
| +{
|
| + for (CDMachOFile *machOFile in machOFiles) {
|
| + CDObjectiveCProcessor *aProcessor;
|
| +
|
| + aProcessor = [[[machOFile processorClass] alloc] initWithMachOFile:machOFile];
|
| + [aProcessor process];
|
| + [objcProcessors addObject:aProcessor];
|
| + [aProcessor release];
|
| + }
|
| +}
|
| +
|
| +// This visits everything segment processors, classes, categories. It skips over modules. Need something to visit modules so we can generate separate headers.
|
| +- (void)recursivelyVisit:(CDVisitor *)aVisitor;
|
| +{
|
| + [aVisitor willBeginVisiting];
|
| +
|
| + if ([self containsObjectiveCData] || [self hasEncryptedFiles]) {
|
| + for (CDObjectiveCProcessor *processor in objcProcessors) {
|
| + [processor recursivelyVisit:aVisitor];
|
| + }
|
| + }
|
| +
|
| + [aVisitor didEndVisiting];
|
| +}
|
| +
|
| +- (CDMachOFile *)machOFileWithID:(NSString *)anID;
|
| +{
|
| + NSString *adjustedID = nil;
|
| + CDMachOFile *aMachOFile;
|
| + NSString *executablePathPrefix = @"@executable_path";
|
| + NSString *rpathPrefix = @"@rpath";
|
| +
|
| + if ([anID hasPrefix:executablePathPrefix]) {
|
| + adjustedID = [anID stringByReplacingOccurrencesOfString:executablePathPrefix withString:searchPathState.executablePath];
|
| + } else if ([anID hasPrefix:rpathPrefix]) {
|
| + //NSLog(@"Searching for %@ through run paths: %@", anID, [searchPathState searchPaths]);
|
| + for (NSString *searchPath in [searchPathState searchPaths]) {
|
| + NSString *str = [anID stringByReplacingOccurrencesOfString:rpathPrefix withString:searchPath];
|
| + //NSLog(@"trying %@", str);
|
| + if ([[NSFileManager defaultManager] fileExistsAtPath:str]) {
|
| + adjustedID = str;
|
| + //NSLog(@"Found it!");
|
| + break;
|
| + }
|
| + }
|
| + if (adjustedID == nil) {
|
| + adjustedID = anID;
|
| + //NSLog(@"Did not find it.");
|
| + }
|
| + } else if (sdkRoot != nil) {
|
| + adjustedID = [sdkRoot stringByAppendingPathComponent:anID];
|
| + } else {
|
| + adjustedID = anID;
|
| + }
|
| +
|
| + aMachOFile = [machOFilesByID objectForKey:adjustedID];
|
| + if (aMachOFile == nil) {
|
| + NSData *data;
|
| + CDFile *aFile;
|
| +
|
| + data = [[NSData alloc] initWithContentsOfMappedFile:adjustedID];
|
| + aFile = [CDFile fileWithData:data filename:adjustedID searchPathState:searchPathState];
|
| + [data release];
|
| +
|
| + if (aFile == nil || [self loadFile:aFile] == NO)
|
| + NSLog(@"Warning: Failed to load: %@", adjustedID);
|
| +
|
| + aMachOFile = [machOFilesByID objectForKey:adjustedID];
|
| + if (aMachOFile == nil) {
|
| + NSLog(@"Warning: Couldn't load MachOFile with ID: %@, adjustedID: %@", anID, adjustedID);
|
| + }
|
| + }
|
| +
|
| + return aMachOFile;
|
| +}
|
| +
|
| +- (void)appendHeaderToString:(NSMutableString *)resultString;
|
| +{
|
| + // Since this changes each version, for regression testing it'll be better to be able to not show it.
|
| + if (flags.shouldShowHeader == NO)
|
| + return;
|
| +
|
| + [resultString appendString:@"/*\n"];
|
| + [resultString appendFormat:@" * Generated by class-dump %s.\n", CLASS_DUMP_VERSION];
|
| + [resultString appendString:@" *\n"];
|
| + [resultString appendString:@" * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2010 by Steve Nygard.\n"];
|
| + [resultString appendString:@" */\n\n"];
|
| +
|
| + if (sdkRoot != nil) {
|
| + [resultString appendString:@"/*\n"];
|
| + [resultString appendFormat:@" * SDK Root: %@\n", sdkRoot];
|
| + [resultString appendString:@" */\n\n"];
|
| + }
|
| +}
|
| +
|
| +- (void)registerTypes;
|
| +{
|
| + for (CDObjectiveCProcessor *processor in objcProcessors) {
|
| + [processor registerTypesWithObject:typeController phase:0];
|
| + }
|
| + [typeController endPhase:0];
|
| +
|
| + [typeController workSomeMagic];
|
| +}
|
| +
|
| +- (void)showHeader;
|
| +{
|
| + if ([machOFiles count] > 0) {
|
| + [[[machOFiles lastObject] headerString:YES] print];
|
| + }
|
| +}
|
| +
|
| +- (void)showLoadCommands;
|
| +{
|
| + if ([machOFiles count] > 0) {
|
| + [[[machOFiles lastObject] loadCommandString:YES] print];
|
| + }
|
| +}
|
| +
|
| +@end
|
|
|
| Property changes on: class-dump/src/CDClassDump.m
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|