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 #include <stdio.h> |
| 7 #include <libc.h> |
| 8 #include <unistd.h> |
| 9 #include <getopt.h> |
| 10 #include <stdlib.h> |
| 11 #include <mach-o/arch.h> |
| 12 |
| 13 #import <Foundation/Foundation.h> |
| 14 #import "NSString-Extensions.h" |
| 15 |
| 16 #import "CDClassDump.h" |
| 17 #import "CDFindMethodVisitor.h" |
| 18 #import "CDClassDumpVisitor.h" |
| 19 #import "CDMultiFileVisitor.h" |
| 20 #import "CDFile.h" |
| 21 #import "CDMachOFile.h" |
| 22 #import "CDFatFile.h" |
| 23 #import "CDObjectiveC2Processor64.h" |
| 24 #import "CDFatArch.h" |
| 25 #import "CDSearchPathState.h" |
| 26 |
| 27 void print_usage(void) |
| 28 { |
| 29 fprintf(stderr, |
| 30 "class-dump %s\n" |
| 31 "Usage: class-dump [options] <mach-o-file>\n" |
| 32 "\n" |
| 33 " where options are:\n" |
| 34 " -a show instance variable offsets\n" |
| 35 " -A show implementation addresses\n" |
| 36 " --arch <arch> choose a specific architecture from a univer
sal binary (ppc, ppc64, i386, x86_64)\n" |
| 37 " -C <regex> only display classes matching regular expres
sion\n" |
| 38 " -f <str> find string in method name\n" |
| 39 " -H generate header files in current directory,
or directory specified with -o\n" |
| 40 " -I sort classes, categories, and protocols by i
nheritance (overrides -s)\n" |
| 41 " -o <dir> output directory used for -H\n" |
| 42 " -r recursively expand frameworks and fixed VM s
hared libraries\n" |
| 43 " -s sort classes and categories by name\n" |
| 44 " -S sort methods by name\n" |
| 45 " -t suppress header in output, for testing\n" |
| 46 " --list-arches list the arches in the file, then exit\n" |
| 47 " --sdk-root specify the SDK root path (full path, or 4.1
, 4.0, 3.2, 10.6, 10.5, 3.1.3, 3.1.2, 3.1)\n" |
| 48 , |
| 49 CLASS_DUMP_VERSION |
| 50 ); |
| 51 } |
| 52 |
| 53 #define CD_OPT_ARCH 1 |
| 54 #define CD_OPT_LIST_ARCHES 2 |
| 55 #define CD_OPT_VERSION 3 |
| 56 #define CD_OPT_SDK_ROOT 4 |
| 57 |
| 58 struct sdk_alias { |
| 59 NSString *alias; |
| 60 NSString *path; |
| 61 }; |
| 62 |
| 63 struct sdk_alias sdk_aliases[] = { |
| 64 { @"3.0", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.
0.sdk", }, |
| 65 { @"3.1", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.
1.sdk", }, |
| 66 { @"3.1.2", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS
3.1.2.sdk", }, |
| 67 { @"3.1.3", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS
3.1.3.sdk", }, |
| 68 { @"3.2", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.
2.sdk", }, |
| 69 { @"4.0", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.
0.sdk", }, |
| 70 { @"4.1", @"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.
1.sdk", }, |
| 71 { @"10.5", @"/Developer/SDKs/MacOSX10.5.sdk", }, |
| 72 { @"10.6", @"/Developer/SDKs/MacOSX10.6.sdk", }, |
| 73 { nil, nil, }, |
| 74 }; |
| 75 |
| 76 // Keyed by alias, value is full path. |
| 77 NSDictionary *sdkAliases(void) |
| 78 { |
| 79 static NSDictionary *aliases = nil; |
| 80 |
| 81 if (aliases == nil) { |
| 82 NSMutableDictionary *dict; |
| 83 struct sdk_alias *ptr = sdk_aliases; |
| 84 |
| 85 dict = [[NSMutableDictionary alloc] init]; |
| 86 while (ptr->alias != nil) { |
| 87 [dict setObject:ptr->path forKey:ptr->alias]; |
| 88 ptr++; |
| 89 } |
| 90 aliases = [dict copy]; |
| 91 [dict release]; |
| 92 } |
| 93 |
| 94 return aliases; |
| 95 } |
| 96 |
| 97 int main(int argc, char *argv[]) |
| 98 { |
| 99 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| 100 CDClassDump *classDump; |
| 101 CDMultiFileVisitor *multiFileVisitor; |
| 102 BOOL shouldFind = NO; |
| 103 NSString *searchString = nil; |
| 104 BOOL shouldGenerateSeparateHeaders = NO; |
| 105 BOOL shouldListArches = NO; |
| 106 BOOL shouldPrintVersion = NO; |
| 107 CDArch targetArch; |
| 108 BOOL hasSpecifiedArch = NO; |
| 109 |
| 110 int ch; |
| 111 BOOL errorFlag = NO; |
| 112 |
| 113 struct option longopts[] = { |
| 114 { "show-ivar-offsets", no_argument, NULL, 'a' }, |
| 115 { "show-imp-addr", no_argument, NULL, 'A' }, |
| 116 { "match", required_argument, NULL, 'C' }, |
| 117 { "find", required_argument, NULL, 'f' }, |
| 118 { "generate-multiple-files", no_argument, NULL, 'H' }, |
| 119 { "sort-by-inheritance", no_argument, NULL, 'I' }, |
| 120 { "output-dir", required_argument, NULL, 'o' }, |
| 121 { "recursive", no_argument, NULL, 'r' }, |
| 122 { "sort", no_argument, NULL, 's' }, |
| 123 { "sort-methods", no_argument, NULL, 'S' }, |
| 124 { "arch", required_argument, NULL, CD_OPT_ARCH }, |
| 125 { "list-arches", no_argument, NULL, CD_OPT_LIST_ARCHES }, |
| 126 { "suppress-header", no_argument, NULL, 't' }, |
| 127 { "version", no_argument, NULL, CD_OPT_VERSION }, |
| 128 { "sdk-root", required_argument, NULL, CD_OPT_SDK_ROOT }, |
| 129 { NULL, 0, NULL, 0 }, |
| 130 }; |
| 131 |
| 132 if (argc == 1) { |
| 133 print_usage(); |
| 134 exit(0); |
| 135 } |
| 136 |
| 137 classDump = [[[CDClassDump alloc] init] autorelease]; |
| 138 multiFileVisitor = [[[CDMultiFileVisitor alloc] init] autorelease]; |
| 139 [multiFileVisitor setClassDump:classDump]; |
| 140 |
| 141 while ( (ch = getopt_long(argc, argv, "aAC:f:HIo:rRsSt", longopts, NULL)) !=
-1) { |
| 142 switch (ch) { |
| 143 case CD_OPT_ARCH: { |
| 144 NSString *name; |
| 145 |
| 146 name = [NSString stringWithUTF8String:optarg]; |
| 147 targetArch = CDArchFromName(name); |
| 148 if (targetArch.cputype != CPU_TYPE_ANY) |
| 149 hasSpecifiedArch = YES; |
| 150 else { |
| 151 fprintf(stderr, "Error: Unknown arch %s\n\n", optarg); |
| 152 errorFlag = YES; |
| 153 } |
| 154 break; |
| 155 } |
| 156 |
| 157 case CD_OPT_LIST_ARCHES: |
| 158 shouldListArches = YES; |
| 159 break; |
| 160 |
| 161 case CD_OPT_VERSION: |
| 162 shouldPrintVersion = YES; |
| 163 break; |
| 164 |
| 165 case CD_OPT_SDK_ROOT: { |
| 166 NSString *root, *str; |
| 167 NSDictionary *aliases = sdkAliases(); |
| 168 |
| 169 root = [NSString stringWithUTF8String:optarg]; |
| 170 //NSLog(@"root: %@", root); |
| 171 //NSLog(@"aliases: %@", aliases); |
| 172 str = [aliases objectForKey:root]; |
| 173 if (str == nil) { |
| 174 classDump.sdkRoot = root; |
| 175 } else { |
| 176 classDump.sdkRoot = str; |
| 177 } |
| 178 |
| 179 break; |
| 180 } |
| 181 |
| 182 case 'a': |
| 183 [classDump setShouldShowIvarOffsets:YES]; |
| 184 break; |
| 185 |
| 186 case 'A': |
| 187 [classDump setShouldShowMethodAddresses:YES]; |
| 188 break; |
| 189 |
| 190 case 'C': { |
| 191 NSString *errorMessage; |
| 192 |
| 193 if ([classDump setRegex:optarg errorMessage:&errorMessage] == NO)
{ |
| 194 fprintf(stderr, "class-dump: Error with regex: '%s'\n\n", [err
orMessage UTF8String]); |
| 195 errorFlag = YES; |
| 196 } |
| 197 // Last one wins now. |
| 198 break; |
| 199 } |
| 200 |
| 201 case 'f': { |
| 202 shouldFind = YES; |
| 203 |
| 204 searchString = [NSString stringWithUTF8String:optarg]; |
| 205 break; |
| 206 } |
| 207 |
| 208 case 'H': |
| 209 shouldGenerateSeparateHeaders = YES; |
| 210 break; |
| 211 |
| 212 case 'I': |
| 213 [classDump setShouldSortClassesByInheritance:YES]; |
| 214 break; |
| 215 |
| 216 case 'o': |
| 217 [multiFileVisitor setOutputPath:[NSString stringWithUTF8String:opt
arg]]; |
| 218 break; |
| 219 |
| 220 case 'r': |
| 221 [classDump setShouldProcessRecursively:YES]; |
| 222 break; |
| 223 |
| 224 case 's': |
| 225 [classDump setShouldSortClasses:YES]; |
| 226 break; |
| 227 |
| 228 case 'S': |
| 229 [classDump setShouldSortMethods:YES]; |
| 230 break; |
| 231 |
| 232 case 't': |
| 233 [classDump setShouldShowHeader:NO]; |
| 234 break; |
| 235 |
| 236 case '?': |
| 237 default: |
| 238 errorFlag = YES; |
| 239 break; |
| 240 } |
| 241 } |
| 242 |
| 243 if (errorFlag) { |
| 244 print_usage(); |
| 245 exit(2); |
| 246 } |
| 247 |
| 248 if (shouldPrintVersion) { |
| 249 printf("class-dump %s compiled %s\n", CLASS_DUMP_VERSION, __DATE__ " " _
_TIME__); |
| 250 exit(0); |
| 251 } |
| 252 |
| 253 if (optind < argc) { |
| 254 NSString *arg, *executablePath; |
| 255 |
| 256 arg = [NSString stringWithFileSystemRepresentation:argv[optind]]; |
| 257 executablePath = [arg executablePathForFilename]; |
| 258 if (shouldListArches) { |
| 259 if (executablePath == nil) { |
| 260 printf("none\n"); |
| 261 } else { |
| 262 CDSearchPathState *searchPathState; |
| 263 NSData *data; |
| 264 id macho; |
| 265 |
| 266 searchPathState = [[[CDSearchPathState alloc] init] autorelease]
; |
| 267 searchPathState.executablePath = executablePath; |
| 268 data = [[NSData alloc] initWithContentsOfMappedFile:executablePa
th]; |
| 269 macho = [CDFile fileWithData:data filename:executablePath search
PathState:searchPathState]; |
| 270 if (macho == nil) { |
| 271 printf("none\n"); |
| 272 } else { |
| 273 if ([macho isKindOfClass:[CDMachOFile class]]) { |
| 274 printf("%s\n", [[macho archName] UTF8String]); |
| 275 } else if ([macho isKindOfClass:[CDFatFile class]]) { |
| 276 printf("%s\n", [[[macho archNames] componentsJoinedByStr
ing:@" "] UTF8String]); |
| 277 } |
| 278 } |
| 279 [data release]; |
| 280 } |
| 281 } else { |
| 282 CDFile *file; |
| 283 NSData *data; |
| 284 |
| 285 if (executablePath == nil) { |
| 286 fprintf(stderr, "class-dump: Input file (%s) doesn't contain an
executable.\n", [arg fileSystemRepresentation]); |
| 287 exit(1); |
| 288 } |
| 289 |
| 290 data = [[NSData alloc] initWithContentsOfMappedFile:executablePath]; |
| 291 if (data == nil) { |
| 292 NSFileManager *defaultManager = [NSFileManager defaultManager]; |
| 293 |
| 294 if ([defaultManager fileExistsAtPath:executablePath]) { |
| 295 fprintf(stderr, "class-dump: Input file (%s) is not readable
(check read rights).\n", [executablePath UTF8String]); |
| 296 } else { |
| 297 fprintf(stderr, "class-dump: Input file (%s) does not exist.
\n", [executablePath UTF8String]); |
| 298 } |
| 299 |
| 300 exit(1); |
| 301 } |
| 302 |
| 303 classDump.searchPathState.executablePath = [executablePath stringByD
eletingLastPathComponent]; |
| 304 file = [CDFile fileWithData:data filename:executablePath searchPathS
tate:classDump.searchPathState]; |
| 305 if (file == nil) { |
| 306 fprintf(stderr, "class-dump: Input file (%s) is neither a Mach-O
file nor a fat archive.\n", [executablePath UTF8String]); |
| 307 [data release]; |
| 308 exit(1); |
| 309 } |
| 310 #if 0 |
| 311 { |
| 312 CDFatFile *fat = file; |
| 313 NSArray *a1; |
| 314 NSUInteger count, index; |
| 315 |
| 316 a1 = [fat arches]; |
| 317 count = [a1 count]; |
| 318 for (index = 0; index < count; index++) |
| 319 [[[a1 objectAtIndex:index] machOData] writeToFile:[NSString
stringWithFormat:@"/tmp/arch-%u", index] atomically:NO]; |
| 320 |
| 321 exit(99); |
| 322 } |
| 323 #endif |
| 324 if (hasSpecifiedArch == NO) { |
| 325 if ([file bestMatchForLocalArch:&targetArch] == NO) { |
| 326 fprintf(stderr, "Error: Couldn't get local architecture\n"); |
| 327 exit(1); |
| 328 } |
| 329 //NSLog(@"No arch specified, best match for local arch is: (%08x
, %08x)", targetArch.cputype, targetArch.cpusubtype); |
| 330 } else { |
| 331 //NSLog(@"chosen arch is: (%08x, %08x)", targetArch.cputype, tar
getArch.cpusubtype); |
| 332 } |
| 333 |
| 334 #ifndef __LP64__ |
| 335 if (CDArchUses64BitABI(targetArch)) { |
| 336 fprintf(stderr, "Error: Can't dump 64-bit files with 32-bit vers
ion of class-dump\n"); |
| 337 exit(1); |
| 338 } |
| 339 #endif |
| 340 |
| 341 [classDump setTargetArch:targetArch]; |
| 342 classDump.searchPathState.executablePath = [executablePath stringByD
eletingLastPathComponent]; |
| 343 |
| 344 if ([classDump loadFile:file]) { |
| 345 #if 0 |
| 346 [classDump showHeader]; |
| 347 [classDump showLoadCommands]; |
| 348 exit(5); |
| 349 #endif |
| 350 |
| 351 [classDump processObjectiveCData]; |
| 352 [classDump registerTypes]; |
| 353 |
| 354 if (shouldFind) { |
| 355 CDFindMethodVisitor *visitor; |
| 356 |
| 357 visitor = [[CDFindMethodVisitor alloc] init]; |
| 358 [visitor setClassDump:classDump]; |
| 359 [visitor setFindString:searchString]; |
| 360 [classDump recursivelyVisit:visitor]; |
| 361 [visitor release]; |
| 362 } else if (shouldGenerateSeparateHeaders) { |
| 363 [classDump recursivelyVisit:multiFileVisitor]; |
| 364 } else { |
| 365 CDClassDumpVisitor *visitor; |
| 366 |
| 367 visitor = [[CDClassDumpVisitor alloc] init]; |
| 368 [visitor setClassDump:classDump]; |
| 369 [classDump recursivelyVisit:visitor]; |
| 370 [visitor release]; |
| 371 } |
| 372 } |
| 373 |
| 374 [data release]; |
| 375 } |
| 376 } |
| 377 |
| 378 [pool release]; |
| 379 |
| 380 return 0; |
| 381 } |
OLD | NEW |