OLD | NEW |
(Empty) | |
| 1 // -*- mode: ObjC -*- |
| 2 |
| 3 // This file is part of class-dump, a utility for examining the Objective-C seg
ment of Mach-O files. |
| 4 // Copyright (C) 1997-1998, 2000-2001, 2004-2010 Steve Nygard. |
| 5 |
| 6 #import "CDClassDump.h" |
| 7 |
| 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" |
| 23 #import "CDSearchPathState.h" |
| 24 |
| 25 @implementation CDClassDump |
| 26 |
| 27 - (id)init; |
| 28 { |
| 29 if ([super init] == nil) |
| 30 return nil; |
| 31 |
| 32 searchPathState = [[CDSearchPathState alloc] init]; |
| 33 sdkRoot = nil; |
| 34 |
| 35 machOFiles = [[NSMutableArray alloc] init]; |
| 36 machOFilesByID = [[NSMutableDictionary alloc] init]; |
| 37 objcProcessors = [[NSMutableArray alloc] init]; |
| 38 |
| 39 typeController = [[CDTypeController alloc] initWithClassDump:self]; |
| 40 |
| 41 // These can be ppc, ppc7400, ppc64, i386, x86_64 |
| 42 targetArch.cputype = CPU_TYPE_ANY; |
| 43 targetArch.cpusubtype = 0; |
| 44 |
| 45 flags.shouldShowHeader = YES; |
| 46 |
| 47 return self; |
| 48 } |
| 49 |
| 50 - (void)dealloc; |
| 51 { |
| 52 [searchPathState release]; |
| 53 [sdkRoot release]; |
| 54 |
| 55 [machOFiles release]; |
| 56 [machOFilesByID release]; |
| 57 [objcProcessors release]; |
| 58 |
| 59 [typeController release]; |
| 60 |
| 61 if (flags.shouldMatchRegex) |
| 62 regfree(&compiledRegex); |
| 63 |
| 64 [super dealloc]; |
| 65 } |
| 66 |
| 67 @synthesize searchPathState; |
| 68 |
| 69 - (BOOL)shouldProcessRecursively; |
| 70 { |
| 71 return flags.shouldProcessRecursively; |
| 72 } |
| 73 |
| 74 - (void)setShouldProcessRecursively:(BOOL)newFlag; |
| 75 { |
| 76 flags.shouldProcessRecursively = newFlag; |
| 77 } |
| 78 |
| 79 - (BOOL)shouldSortClasses; |
| 80 { |
| 81 return flags.shouldSortClasses; |
| 82 } |
| 83 |
| 84 - (void)setShouldSortClasses:(BOOL)newFlag; |
| 85 { |
| 86 flags.shouldSortClasses = newFlag; |
| 87 } |
| 88 |
| 89 - (BOOL)shouldSortClassesByInheritance; |
| 90 { |
| 91 return flags.shouldSortClassesByInheritance; |
| 92 } |
| 93 |
| 94 - (void)setShouldSortClassesByInheritance:(BOOL)newFlag; |
| 95 { |
| 96 flags.shouldSortClassesByInheritance = newFlag; |
| 97 } |
| 98 |
| 99 - (BOOL)shouldSortMethods; |
| 100 { |
| 101 return flags.shouldSortMethods; |
| 102 } |
| 103 |
| 104 - (void)setShouldSortMethods:(BOOL)newFlag; |
| 105 { |
| 106 flags.shouldSortMethods = newFlag; |
| 107 } |
| 108 |
| 109 - (BOOL)shouldShowIvarOffsets; |
| 110 { |
| 111 return flags.shouldShowIvarOffsets; |
| 112 } |
| 113 |
| 114 - (void)setShouldShowIvarOffsets:(BOOL)newFlag; |
| 115 { |
| 116 flags.shouldShowIvarOffsets = newFlag; |
| 117 } |
| 118 |
| 119 - (BOOL)shouldShowMethodAddresses; |
| 120 { |
| 121 return flags.shouldShowMethodAddresses; |
| 122 } |
| 123 |
| 124 - (void)setShouldShowMethodAddresses:(BOOL)newFlag; |
| 125 { |
| 126 flags.shouldShowMethodAddresses = newFlag; |
| 127 } |
| 128 |
| 129 - (BOOL)shouldMatchRegex; |
| 130 { |
| 131 return flags.shouldMatchRegex; |
| 132 } |
| 133 |
| 134 - (void)setShouldMatchRegex:(BOOL)newFlag; |
| 135 { |
| 136 if (flags.shouldMatchRegex && newFlag == NO) |
| 137 regfree(&compiledRegex); |
| 138 |
| 139 flags.shouldMatchRegex = newFlag; |
| 140 } |
| 141 |
| 142 - (BOOL)shouldShowHeader; |
| 143 { |
| 144 return flags.shouldShowHeader; |
| 145 } |
| 146 |
| 147 - (void)setShouldShowHeader:(BOOL)newFlag; |
| 148 { |
| 149 flags.shouldShowHeader = newFlag; |
| 150 } |
| 151 |
| 152 - (BOOL)setRegex:(char *)regexCString errorMessage:(NSString **)errorMessagePoin
ter; |
| 153 { |
| 154 int result; |
| 155 |
| 156 if (flags.shouldMatchRegex) |
| 157 regfree(&compiledRegex); |
| 158 |
| 159 result = regcomp(&compiledRegex, regexCString, REG_EXTENDED); |
| 160 if (result != 0) { |
| 161 char regex_error_buffer[256]; |
| 162 |
| 163 if (regerror(result, &compiledRegex, regex_error_buffer, 256) > 0) { |
| 164 if (errorMessagePointer != NULL) { |
| 165 *errorMessagePointer = [NSString stringWithUTF8String:regex_erro
r_buffer]; |
| 166 } |
| 167 } else { |
| 168 if (errorMessagePointer != NULL) |
| 169 *errorMessagePointer = nil; |
| 170 } |
| 171 |
| 172 return NO; |
| 173 } |
| 174 |
| 175 [self setShouldMatchRegex:YES]; |
| 176 |
| 177 return YES; |
| 178 } |
| 179 |
| 180 - (BOOL)regexMatchesString:(NSString *)aString; |
| 181 { |
| 182 int result; |
| 183 |
| 184 result = regexec(&compiledRegex, [aString UTF8String], 0, NULL, 0); |
| 185 if (result != 0) { |
| 186 if (result != REG_NOMATCH) { |
| 187 char regex_error_buffer[256]; |
| 188 |
| 189 if (regerror(result, &compiledRegex, regex_error_buffer, 256) > 0) |
| 190 NSLog(@"Error with regex matching string, %@", [NSString stringW
ithUTF8String:regex_error_buffer]); |
| 191 } |
| 192 |
| 193 return NO; |
| 194 } |
| 195 |
| 196 return YES; |
| 197 } |
| 198 |
| 199 @synthesize sdkRoot; |
| 200 |
| 201 - (NSArray *)machOFiles; |
| 202 { |
| 203 return machOFiles; |
| 204 } |
| 205 |
| 206 - (NSArray *)objcProcessors; |
| 207 { |
| 208 return objcProcessors; |
| 209 } |
| 210 |
| 211 @synthesize targetArch; |
| 212 |
| 213 - (BOOL)containsObjectiveCData; |
| 214 { |
| 215 for (CDObjectiveCProcessor *processor in objcProcessors) { |
| 216 if ([processor hasObjectiveCData]) |
| 217 return YES; |
| 218 } |
| 219 |
| 220 return NO; |
| 221 } |
| 222 |
| 223 - (BOOL)hasEncryptedFiles; |
| 224 { |
| 225 for (CDMachOFile *machOFile in machOFiles) { |
| 226 if ([machOFile isEncrypted]) { |
| 227 return YES; |
| 228 } |
| 229 } |
| 230 |
| 231 return NO; |
| 232 } |
| 233 |
| 234 - (CDTypeController *)typeController; |
| 235 { |
| 236 return typeController; |
| 237 } |
| 238 |
| 239 - (BOOL)loadFile:(CDFile *)aFile; |
| 240 { |
| 241 CDMachOFile *aMachOFile; |
| 242 |
| 243 //NSLog(@"targetArch: (%08x, %08x)", targetArch.cputype, targetArch.cpusubty
pe); |
| 244 aMachOFile = [aFile machOFileWithArch:targetArch]; |
| 245 //NSLog(@"aMachOFile: %@", aMachOFile); |
| 246 if (aMachOFile == nil) { |
| 247 fprintf(stderr, "Error: file doesn't contain the specified arch.\n\n"); |
| 248 return NO; |
| 249 } |
| 250 |
| 251 // Set before processing recursively. This was getting caught on CoreUI on
10.6 |
| 252 assert([aMachOFile filename] != nil); |
| 253 [machOFiles addObject:aMachOFile]; |
| 254 [machOFilesByID setObject:aMachOFile forKey:[aMachOFile filename]]; |
| 255 |
| 256 if ([self shouldProcessRecursively]) { |
| 257 @try { |
| 258 for (CDLoadCommand *loadCommand in [aMachOFile loadCommands]) { |
| 259 if ([loadCommand isKindOfClass:[CDLCDylib class]]) { |
| 260 CDLCDylib *aDylibCommand; |
| 261 |
| 262 aDylibCommand = (CDLCDylib *)loadCommand; |
| 263 if ([aDylibCommand cmd] == LC_LOAD_DYLIB) { |
| 264 [searchPathState pushSearchPaths:[aMachOFile runPaths]]; |
| 265 [self machOFileWithID:[aDylibCommand path]]; // Loads as
a side effect |
| 266 [searchPathState popSearchPaths]; |
| 267 } |
| 268 } |
| 269 } |
| 270 } |
| 271 @catch (NSException *exception) { |
| 272 [aMachOFile release]; |
| 273 return NO; |
| 274 } |
| 275 } |
| 276 |
| 277 return YES; |
| 278 } |
| 279 |
| 280 - (void)processObjectiveCData; |
| 281 { |
| 282 for (CDMachOFile *machOFile in machOFiles) { |
| 283 CDObjectiveCProcessor *aProcessor; |
| 284 |
| 285 aProcessor = [[[machOFile processorClass] alloc] initWithMachOFile:machO
File]; |
| 286 [aProcessor process]; |
| 287 [objcProcessors addObject:aProcessor]; |
| 288 [aProcessor release]; |
| 289 } |
| 290 } |
| 291 |
| 292 // This visits everything segment processors, classes, categories. It skips ove
r modules. Need something to visit modules so we can generate separate headers. |
| 293 - (void)recursivelyVisit:(CDVisitor *)aVisitor; |
| 294 { |
| 295 [aVisitor willBeginVisiting]; |
| 296 |
| 297 if ([self containsObjectiveCData] || [self hasEncryptedFiles]) { |
| 298 for (CDObjectiveCProcessor *processor in objcProcessors) { |
| 299 [processor recursivelyVisit:aVisitor]; |
| 300 } |
| 301 } |
| 302 |
| 303 [aVisitor didEndVisiting]; |
| 304 } |
| 305 |
| 306 - (CDMachOFile *)machOFileWithID:(NSString *)anID; |
| 307 { |
| 308 NSString *adjustedID = nil; |
| 309 CDMachOFile *aMachOFile; |
| 310 NSString *executablePathPrefix = @"@executable_path"; |
| 311 NSString *rpathPrefix = @"@rpath"; |
| 312 |
| 313 if ([anID hasPrefix:executablePathPrefix]) { |
| 314 adjustedID = [anID stringByReplacingOccurrencesOfString:executablePathPr
efix withString:searchPathState.executablePath]; |
| 315 } else if ([anID hasPrefix:rpathPrefix]) { |
| 316 //NSLog(@"Searching for %@ through run paths: %@", anID, [searchPathStat
e searchPaths]); |
| 317 for (NSString *searchPath in [searchPathState searchPaths]) { |
| 318 NSString *str = [anID stringByReplacingOccurrencesOfString:rpathPref
ix withString:searchPath]; |
| 319 //NSLog(@"trying %@", str); |
| 320 if ([[NSFileManager defaultManager] fileExistsAtPath:str]) { |
| 321 adjustedID = str; |
| 322 //NSLog(@"Found it!"); |
| 323 break; |
| 324 } |
| 325 } |
| 326 if (adjustedID == nil) { |
| 327 adjustedID = anID; |
| 328 //NSLog(@"Did not find it."); |
| 329 } |
| 330 } else if (sdkRoot != nil) { |
| 331 adjustedID = [sdkRoot stringByAppendingPathComponent:anID]; |
| 332 } else { |
| 333 adjustedID = anID; |
| 334 } |
| 335 |
| 336 aMachOFile = [machOFilesByID objectForKey:adjustedID]; |
| 337 if (aMachOFile == nil) { |
| 338 NSData *data; |
| 339 CDFile *aFile; |
| 340 |
| 341 data = [[NSData alloc] initWithContentsOfMappedFile:adjustedID]; |
| 342 aFile = [CDFile fileWithData:data filename:adjustedID searchPathState:se
archPathState]; |
| 343 [data release]; |
| 344 |
| 345 if (aFile == nil || [self loadFile:aFile] == NO) |
| 346 NSLog(@"Warning: Failed to load: %@", adjustedID); |
| 347 |
| 348 aMachOFile = [machOFilesByID objectForKey:adjustedID]; |
| 349 if (aMachOFile == nil) { |
| 350 NSLog(@"Warning: Couldn't load MachOFile with ID: %@, adjustedID: %@
", anID, adjustedID); |
| 351 } |
| 352 } |
| 353 |
| 354 return aMachOFile; |
| 355 } |
| 356 |
| 357 - (void)appendHeaderToString:(NSMutableString *)resultString; |
| 358 { |
| 359 // Since this changes each version, for regression testing it'll be better t
o be able to not show it. |
| 360 if (flags.shouldShowHeader == NO) |
| 361 return; |
| 362 |
| 363 [resultString appendString:@"/*\n"]; |
| 364 [resultString appendFormat:@" * Generated by class-dump %s.\n", CLASS_DU
MP_VERSION]; |
| 365 [resultString appendString:@" *\n"]; |
| 366 [resultString appendString:@" * class-dump is Copyright (C) 1997-1998, 2
000-2001, 2004-2010 by Steve Nygard.\n"]; |
| 367 [resultString appendString:@" */\n\n"]; |
| 368 |
| 369 if (sdkRoot != nil) { |
| 370 [resultString appendString:@"/*\n"]; |
| 371 [resultString appendFormat:@" * SDK Root: %@\n", sdkRoot]; |
| 372 [resultString appendString:@" */\n\n"]; |
| 373 } |
| 374 } |
| 375 |
| 376 - (void)registerTypes; |
| 377 { |
| 378 for (CDObjectiveCProcessor *processor in objcProcessors) { |
| 379 [processor registerTypesWithObject:typeController phase:0]; |
| 380 } |
| 381 [typeController endPhase:0]; |
| 382 |
| 383 [typeController workSomeMagic]; |
| 384 } |
| 385 |
| 386 - (void)showHeader; |
| 387 { |
| 388 if ([machOFiles count] > 0) { |
| 389 [[[machOFiles lastObject] headerString:YES] print]; |
| 390 } |
| 391 } |
| 392 |
| 393 - (void)showLoadCommands; |
| 394 { |
| 395 if ([machOFiles count] > 0) { |
| 396 [[[machOFiles lastObject] loadCommandString:YES] print]; |
| 397 } |
| 398 } |
| 399 |
| 400 @end |
OLD | NEW |