Updated a few copyrights I missed.
[class-dump.git] / CDClassDump.m
blob39fbe544fb36a9195c6b6621fedc60ad29ea24d5
1 // -*- mode: ObjC -*-
3 //  This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
4 //  Copyright (C) 1997-1998, 2000-2001, 2004-2010 Steve Nygard.
6 #import "CDClassDump.h"
8 #import "NSArray-Extensions.h"
9 #import "NSString-Extensions.h"
10 #import "CDFatArch.h"
11 #import "CDFatFile.h"
12 #import "CDLCDylib.h"
13 #import "CDMachOFile.h"
14 #import "CDObjectiveCProcessor.h"
15 #import "CDStructureTable.h"
16 #import "CDSymbolReferences.h"
17 #import "CDType.h"
18 #import "CDTypeFormatter.h"
19 #import "CDTypeParser.h"
20 #import "CDVisitor.h"
21 #import "CDLCSegment.h"
22 #import "CDTypeController.h"
24 @implementation CDClassDump
26 - (id)init;
28     if ([super init] == nil)
29         return nil;
31     executablePath = nil;
33     machOFiles = [[NSMutableArray alloc] init];
34     machOFilesByID = [[NSMutableDictionary alloc] init];
35     objcProcessors = [[NSMutableArray alloc] init];
37     typeController = [[CDTypeController alloc] init];
38     [typeController setClassDump:self];
40     // These can be ppc, ppc7400, ppc64, i386, x86_64
41     targetArch.cputype = CPU_TYPE_ANY;
42     targetArch.cpusubtype = 0;
44     flags.shouldShowHeader = YES;
46     return self;
49 - (void)dealloc;
51     [executablePath release];
53     [machOFiles release];
54     [machOFilesByID release];
55     [objcProcessors release];
57     [typeController release];
59     if (flags.shouldMatchRegex)
60         regfree(&compiledRegex);
62     [super dealloc];
65 @synthesize executablePath;
67 - (BOOL)shouldProcessRecursively;
69     return flags.shouldProcessRecursively;
72 - (void)setShouldProcessRecursively:(BOOL)newFlag;
74     flags.shouldProcessRecursively = newFlag;
77 - (BOOL)shouldSortClasses;
79     return flags.shouldSortClasses;
82 - (void)setShouldSortClasses:(BOOL)newFlag;
84     flags.shouldSortClasses = newFlag;
87 - (BOOL)shouldSortClassesByInheritance;
89     return flags.shouldSortClassesByInheritance;
92 - (void)setShouldSortClassesByInheritance:(BOOL)newFlag;
94     flags.shouldSortClassesByInheritance = newFlag;
97 - (BOOL)shouldSortMethods;
99     return flags.shouldSortMethods;
102 - (void)setShouldSortMethods:(BOOL)newFlag;
104     flags.shouldSortMethods = newFlag;
107 - (BOOL)shouldShowIvarOffsets;
109     return flags.shouldShowIvarOffsets;
112 - (void)setShouldShowIvarOffsets:(BOOL)newFlag;
114     flags.shouldShowIvarOffsets = newFlag;
117 - (BOOL)shouldShowMethodAddresses;
119     return flags.shouldShowMethodAddresses;
122 - (void)setShouldShowMethodAddresses:(BOOL)newFlag;
124     flags.shouldShowMethodAddresses = newFlag;
127 - (BOOL)shouldMatchRegex;
129     return flags.shouldMatchRegex;
132 - (void)setShouldMatchRegex:(BOOL)newFlag;
134     if (flags.shouldMatchRegex && newFlag == NO)
135         regfree(&compiledRegex);
137     flags.shouldMatchRegex = newFlag;
140 - (BOOL)shouldShowHeader;
142     return flags.shouldShowHeader;
145 - (void)setShouldShowHeader:(BOOL)newFlag;
147     flags.shouldShowHeader = newFlag;
150 - (BOOL)setRegex:(char *)regexCString errorMessage:(NSString **)errorMessagePointer;
152     int result;
154     if (flags.shouldMatchRegex)
155         regfree(&compiledRegex);
157     result = regcomp(&compiledRegex, regexCString, REG_EXTENDED);
158     if (result != 0) {
159         char regex_error_buffer[256];
161         if (regerror(result, &compiledRegex, regex_error_buffer, 256) > 0) {
162             if (errorMessagePointer != NULL) {
163                 *errorMessagePointer = [NSString stringWithUTF8String:regex_error_buffer];
164             }
165         } else {
166             if (errorMessagePointer != NULL)
167                 *errorMessagePointer = nil;
168         }
170         return NO;
171     }
173     [self setShouldMatchRegex:YES];
175     return YES;
178 - (BOOL)regexMatchesString:(NSString *)aString;
180     int result;
182     result = regexec(&compiledRegex, [aString UTF8String], 0, NULL, 0);
183     if (result != 0) {
184         if (result != REG_NOMATCH) {
185             char regex_error_buffer[256];
187             if (regerror(result, &compiledRegex, regex_error_buffer, 256) > 0)
188                 NSLog(@"Error with regex matching string, %@", [NSString stringWithUTF8String:regex_error_buffer]);
189         }
191         return NO;
192     }
194     return YES;
197 - (NSArray *)machOFiles;
199     return machOFiles;
202 - (NSArray *)objcProcessors;
204     return objcProcessors;
207 @synthesize targetArch;
209 - (BOOL)containsObjectiveCData;
211     for (CDObjectiveCProcessor *processor in objcProcessors) {
212         if ([processor hasObjectiveCData])
213             return YES;
214     }
216     return NO;
219 - (BOOL)hasEncryptedFiles;
221     for (CDMachOFile *machOFile in machOFiles) {
222         if ([machOFile isEncrypted]) {
223             return YES;
224         }
225     }
227     return NO;
230 - (CDTypeController *)typeController;
232     return typeController;
235 // Return YES if successful, NO if there was an error.
236 - (BOOL)_loadFilename:(NSString *)aFilename;
238     NSData *data;
239     CDFile *aFile;
241     data = [[NSData alloc] initWithContentsOfMappedFile:aFilename];
243     aFile = [CDFile fileWithData:data];
244     [aFile setFilename:aFilename];
246     [data release];
248     if (aFile == nil)
249         return NO;
251     return [self loadFile:aFile];
254 - (BOOL)loadFile:(CDFile *)aFile;
256     CDMachOFile *aMachOFile;
258     //NSLog(@"targetArch: (%08x, %08x)", targetArch.cputype, targetArch.cpusubtype);
259     aMachOFile = [aFile machOFileWithArch:targetArch];
260     //NSLog(@"aMachOFile: %@", aMachOFile);
261     if (aMachOFile == nil) {
262         fprintf(stderr, "Error: file doesn't contain the specified arch.\n\n");
263         return NO;
264     }
266     // Set before processing recursively.  This was getting caught on CoreUI on 10.6
267     assert([aMachOFile filename] != nil);
268     [machOFiles addObject:aMachOFile];
269     [machOFilesByID setObject:aMachOFile forKey:[aMachOFile filename]];
271     if ([self shouldProcessRecursively]) {
272         @try {
273             for (CDLoadCommand *loadCommand in [aMachOFile loadCommands]) {
274                 if ([loadCommand isKindOfClass:[CDLCDylib class]]) {
275                     CDLCDylib *aDylibCommand;
277                     aDylibCommand = (CDLCDylib *)loadCommand;
278                     if ([aDylibCommand cmd] == LC_LOAD_DYLIB)
279                         [self machOFileWithID:[aDylibCommand name]]; // Loads as a side effect
280                 }
281             }
282         }
283         @catch (NSException *exception) {
284             [aMachOFile release];
285             return NO;
286         }
287     }
289     return YES;
292 - (void)processObjectiveCData;
294     for (CDMachOFile *machOFile in machOFiles) {
295         CDObjectiveCProcessor *aProcessor;
297         aProcessor = [[[machOFile processorClass] alloc] initWithMachOFile:machOFile];
298         [aProcessor process];
299         [objcProcessors addObject:aProcessor];
300         [aProcessor release];
301     }
304 // This visits everything segment processors, classes, categories.  It skips over modules.  Need something to visit modules so we can generate separate headers.
305 - (void)recursivelyVisit:(CDVisitor *)aVisitor;
307     [aVisitor willBeginVisiting];
309     if ([self containsObjectiveCData] || [self hasEncryptedFiles]) {
310         for (CDObjectiveCProcessor *processor in objcProcessors) {
311             [processor recursivelyVisit:aVisitor];
312         }
313     }
315     [aVisitor didEndVisiting];
318 - (CDMachOFile *)machOFileWithID:(NSString *)anID;
320     NSString *adjustedID;
321     CDMachOFile *aMachOFile;
322     NSString *replacementString = @"@executable_path";
324     if ([anID hasPrefix:replacementString]) {
325         adjustedID = [executablePath stringByAppendingString:[anID substringFromIndex:[replacementString length]]];
326     } else {
327         adjustedID = anID;
328     }
330     aMachOFile = [machOFilesByID objectForKey:adjustedID];
331     if (aMachOFile == nil) {
332         if ([self _loadFilename:adjustedID] == NO)
333             NSLog(@"Warning: Failed to load: %@", adjustedID);
334         aMachOFile = [machOFilesByID objectForKey:adjustedID];
335         if (aMachOFile == nil) {
336             NSLog(@"Warning: Couldn't load MachOFile with ID: %@, adjustedID: %@", anID, adjustedID);
337         }
338     }
340     return aMachOFile;
343 - (void)appendHeaderToString:(NSMutableString *)resultString;
345     // Since this changes each version, for regression testing it'll be better to be able to not show it.
346     if (flags.shouldShowHeader == NO)
347         return;
349     [resultString appendString:@"/*\n"];
350     [resultString appendFormat:@" *     Generated by class-dump %s.\n", CLASS_DUMP_VERSION];
351     [resultString appendString:@" *\n"];
352     [resultString appendString:@" *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2010 by Steve Nygard.\n"];
353     [resultString appendString:@" */\n\n"];
356 - (void)registerTypes;
358     for (CDObjectiveCProcessor *processor in objcProcessors) {
359         [processor registerTypesWithObject:typeController phase:0];
360     }
361     [typeController endPhase:0];
363     [typeController workSomeMagic];
366 - (void)showHeader;
368     if ([machOFiles count] > 0) {
369         [[[machOFiles lastObject] headerString:YES] print];
370     }
373 - (void)showLoadCommands;
375     if ([machOFiles count] > 0) {
376         [[[machOFiles lastObject] loadCommandString:YES] print];
377     }
380 @end