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"
13 #import "CDMachOFile.h"
14 #import "CDObjectiveCProcessor.h"
15 #import "CDStructureTable.h"
16 #import "CDSymbolReferences.h"
18 #import "CDTypeFormatter.h"
19 #import "CDTypeParser.h"
21 #import "CDLCSegment.h"
22 #import "CDTypeController.h"
24 @implementation CDClassDump
28 if ([super init] == 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;
51 [executablePath release];
54 [machOFilesByID release];
55 [objcProcessors release];
57 [typeController release];
59 if (flags.shouldMatchRegex)
60 regfree(&compiledRegex);
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;
154 if (flags.shouldMatchRegex)
155 regfree(&compiledRegex);
157 result = regcomp(&compiledRegex, regexCString, REG_EXTENDED);
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];
166 if (errorMessagePointer != NULL)
167 *errorMessagePointer = nil;
173 [self setShouldMatchRegex:YES];
178 - (BOOL)regexMatchesString:(NSString *)aString;
182 result = regexec(&compiledRegex, [aString UTF8String], 0, NULL, 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]);
197 - (NSArray *)machOFiles;
202 - (NSArray *)objcProcessors;
204 return objcProcessors;
207 @synthesize targetArch;
209 - (BOOL)containsObjectiveCData;
211 for (CDObjectiveCProcessor *processor in objcProcessors) {
212 if ([processor hasObjectiveCData])
219 - (BOOL)hasEncryptedFiles;
221 for (CDMachOFile *machOFile in machOFiles) {
222 if ([machOFile isEncrypted]) {
230 - (CDTypeController *)typeController;
232 return typeController;
235 // Return YES if successful, NO if there was an error.
236 - (BOOL)_loadFilename:(NSString *)aFilename;
241 data = [[NSData alloc] initWithContentsOfMappedFile:aFilename];
243 aFile = [CDFile fileWithData:data];
244 [aFile setFilename:aFilename];
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");
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]) {
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
283 @catch (NSException *exception) {
284 [aMachOFile release];
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];
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];
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]]];
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);
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)
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];
361 [typeController endPhase:0];
363 [typeController workSomeMagic];
368 if ([machOFiles count] > 0) {
369 [[[machOFiles lastObject] headerString:YES] print];
373 - (void)showLoadCommands;
375 if ([machOFiles count] > 0) {
376 [[[machOFiles lastObject] loadCommandString:YES] print];