blob: 5fa300ad17c0fcfc4699334999f624fb56745026 [file] [log] [blame]
// -*- 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-2009 Steve Nygard.
#import "CDObjectiveC2Processor32.h"
#import "CDMachOFile.h"
#import "CDSection.h"
#import "CDLCSegment.h"
#import "CDDataCursor.h"
#import "CDOCClass.h"
#import "CDOCMethod.h"
#import "CDVisitor.h"
#import "CDOCIvar.h"
#import "NSArray-Extensions.h"
#import "CDLCDynamicSymbolTable.h"
#import "CDLCSymbolTable.h"
#import "CDOCCategory.h"
#import "CDClassDump.h"
#import "CDRelocationInfo.h"
#import "CDSymbol.h"
#import "CDOCProperty.h"
#import "cd_objc2.h"
@implementation CDObjectiveC2Processor32
- (void)loadProtocols;
{
CDLCSegment *segment;
CDSection *section;
NSData *sectionData;
CDDataCursor *cursor;
segment = [machOFile segmentWithName:@"__DATA"];
section = [segment sectionWithName:@"__objc_protolist"];
sectionData = [section data];
cursor = [[CDDataCursor alloc] initWithData:sectionData];
[cursor setByteOrder:[machOFile byteOrder]];
while ([cursor isAtEnd] == NO)
[self protocolAtAddress:[cursor readInt32]];
[cursor release];
[self createUniquedProtocols];
}
- (void)loadClasses;
{
CDLCSegment *segment;
CDSection *section;
NSData *sectionData;
CDDataCursor *cursor;
segment = [machOFile segmentWithName:@"__DATA"];
section = [segment sectionWithName:@"__objc_classlist"];
sectionData = [section data];
cursor = [[CDDataCursor alloc] initWithData:sectionData];
[cursor setByteOrder:[machOFile byteOrder]];
while ([cursor isAtEnd] == NO) {
uint32_t val;
CDOCClass *aClass;
val = [cursor readInt32];
aClass = [self loadClassAtAddress:val];
if (aClass != nil) {
[classes addObject:aClass];
[classesByAddress setObject:aClass forKey:[NSNumber numberWithUnsignedInteger:val]];
}
}
[cursor release];
}
- (void)loadCategories;
{
CDLCSegment *segment;
CDSection *section;
NSData *sectionData;
CDDataCursor *cursor;
segment = [machOFile segmentWithName:@"__DATA"];
section = [segment sectionWithName:@"__objc_catlist"];
sectionData = [section data];
cursor = [[CDDataCursor alloc] initWithData:sectionData];
[cursor setByteOrder:[machOFile byteOrder]];
while ([cursor isAtEnd] == NO) {
CDOCCategory *category;
category = [self loadCategoryAtAddress:[cursor readInt32]];
if (category != nil)
[categories addObject:category];
}
[cursor release];
}
- (CDOCProtocol *)protocolAtAddress:(uint32_t)address;
{
NSNumber *key;
CDOCProtocol *protocol;
if (address == 0)
return nil;
key = [NSNumber numberWithUnsignedInteger:address];
protocol = [protocolsByAddress objectForKey:key];
if (protocol == nil) {
struct cd_objc2_protocol_32 objc2Protocol;
CDDataCursor *cursor;
NSString *str;
protocol = [[[CDOCProtocol alloc] init] autorelease];
[protocolsByAddress setObject:protocol forKey:key];
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
objc2Protocol.isa = [cursor readInt32];
objc2Protocol.name = [cursor readInt32];
objc2Protocol.protocols = [cursor readInt32];
objc2Protocol.instanceMethods = [cursor readInt32];
objc2Protocol.classMethods = [cursor readInt32];
objc2Protocol.optionalInstanceMethods = [cursor readInt32];
objc2Protocol.optionalClassMethods = [cursor readInt32];
objc2Protocol.instanceProperties = [cursor readInt32];
//NSLog(@"----------------------------------------");
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Protocol.isa, objc2Protocol.name, objc2Protocol.protocols, objc2Protocol.instanceMethods);
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Protocol.classMethods, objc2Protocol.optionalInstanceMethods, objc2Protocol.optionalClassMethods, objc2Protocol.instanceProperties);
str = [machOFile stringAtAddress:objc2Protocol.name];
[protocol setName:str];
if (objc2Protocol.protocols != 0) {
uint32_t count, index;
[cursor setOffset:[machOFile dataOffsetForAddress:objc2Protocol.protocols]];
count = [cursor readInt32];
for (index = 0; index < count; index++) {
uint32_t val;
CDOCProtocol *anotherProtocol;
val = [cursor readInt32];
anotherProtocol = [self protocolAtAddress:val];
if (anotherProtocol != nil) {
[protocol addProtocol:anotherProtocol];
} else {
NSLog(@"Note: another protocol was nil.");
}
}
}
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.instanceMethods])
[protocol addInstanceMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.classMethods])
[protocol addClassMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.optionalInstanceMethods])
[protocol addOptionalInstanceMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.optionalClassMethods])
[protocol addOptionalClassMethod:method];
[cursor release];
}
return protocol;
}
- (CDOCCategory *)loadCategoryAtAddress:(uint32_t)address;
{
struct cd_objc2_category_32 objc2Category;
CDDataCursor *cursor;
NSString *str;
CDOCCategory *category;
if (address == 0)
return nil;
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
objc2Category.name = [cursor readInt32];
objc2Category.class = [cursor readInt32];
objc2Category.instanceMethods = [cursor readInt32];
objc2Category.classMethods = [cursor readInt32];
objc2Category.v5 = [cursor readInt32];
objc2Category.v6 = [cursor readInt32];
objc2Category.v7 = [cursor readInt32];
objc2Category.v8 = [cursor readInt32];
//NSLog(@"----------------------------------------");
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Category.name, objc2Category.class, objc2Category.instanceMethods, objc2Category.classMethods);
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Category.v5, objc2Category.v6, objc2Category.v7, objc2Category.v8);
category = [[[CDOCCategory alloc] init] autorelease];
str = [machOFile stringAtAddress:objc2Category.name];
[category setName:str];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Category.instanceMethods])
[category addInstanceMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Category.classMethods])
[category addClassMethod:method];
if ([machOFile hasRelocationEntryForAddress:address + sizeof(objc2Category.name)]) {
[category setClassName:[machOFile externalClassNameForAddress:address + sizeof(objc2Category.name)]];
//NSLog(@"got external class name (%@) for category.", [category className]);
} else if (objc2Category.class != 0) {
CDOCClass *aClass;
aClass = [classesByAddress objectForKey:[NSNumber numberWithUnsignedInteger:objc2Category.class]];
[category setClassName:[aClass name]];
}
[cursor release];
return category;
}
- (CDOCClass *)loadClassAtAddress:(uint32_t)address;
{
struct cd_objc2_class_32 objc2Class;
struct cd_objc2_class_ro_t_32 objc2ClassData;
CDDataCursor *cursor;
NSString *str;
CDOCClass *aClass;
if (address == 0)
return nil;
//NSLog(@"%s, address=%08lx", _cmd, address);
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
objc2Class.isa = [cursor readInt32];
objc2Class.superclass = [cursor readInt32];
objc2Class.cache = [cursor readInt32];
objc2Class.vtable = [cursor readInt32];
objc2Class.data = [cursor readInt32];
objc2Class.reserved1 = [cursor readInt32];
objc2Class.reserved2 = [cursor readInt32];
objc2Class.reserved3 = [cursor readInt32];
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Class.isa, objc2Class.superclass, objc2Class.cache, objc2Class.vtable);
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Class.data, objc2Class.reserved1, objc2Class.reserved2, objc2Class.reserved3);
NSParameterAssert(objc2Class.data != 0);
[cursor setOffset:[machOFile dataOffsetForAddress:objc2Class.data]];
objc2ClassData.flags = [cursor readInt32];
objc2ClassData.instanceStart = [cursor readInt32];
objc2ClassData.instanceSize = [cursor readInt32];
//objc2ClassData.reserved = [cursor readInt32];
objc2ClassData.reserved = 0;
objc2ClassData.ivarLayout = [cursor readInt32];
objc2ClassData.name = [cursor readInt32];
objc2ClassData.baseMethods = [cursor readInt32];
objc2ClassData.baseProtocols = [cursor readInt32];
objc2ClassData.ivars = [cursor readInt32];
objc2ClassData.weakIvarLayout = [cursor readInt32];
objc2ClassData.baseProperties = [cursor readInt32];
//NSLog(@"%08x %08x %08x %08x", objc2ClassData.flags, objc2ClassData.instanceStart, objc2ClassData.instanceSize, objc2ClassData.reserved);
//NSLog(@"%08lx %08lx %08lx %08lx", objc2ClassData.ivarLayout, objc2ClassData.name, objc2ClassData.baseMethods, objc2ClassData.baseProtocols);
//NSLog(@"%08lx %08lx %08lx %08lx", objc2ClassData.ivars, objc2ClassData.weakIvarLayout, objc2ClassData.baseProperties);
str = [machOFile stringAtAddress:objc2ClassData.name];
//NSLog(@"name = %@", str);
aClass = [[[CDOCClass alloc] init] autorelease];
[aClass setName:str];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2ClassData.baseMethods])
[aClass addInstanceMethod:method];
[aClass setIvars:[self loadIvarsAtAddress:objc2ClassData.ivars]];
[cursor release];
if ([machOFile hasRelocationEntryForAddress:address + sizeof(objc2Class.isa)]) {
[aClass setSuperClassName:[machOFile externalClassNameForAddress:address + sizeof(objc2Class.isa)]];
//NSLog(@"got external class name: %@", [aClass superClassName]);
} else if (objc2Class.superclass != 0) {
CDOCClass *sc;
sc = [self loadClassAtAddress:objc2Class.superclass];
[aClass setSuperClassName:[sc name]];
}
for (CDOCMethod *method in [self loadMethodsOfMetaClassAtAddress:objc2Class.isa])
[aClass addClassMethod:method];
// Process protocols
for (CDOCProtocol *protocol in [self uniquedProtocolListAtAddress:objc2ClassData.baseProtocols])
[aClass addProtocol:protocol];
for (CDOCProperty *property in [self loadPropertiesAtAddress:objc2ClassData.baseProperties])
[aClass addProperty:property];
return aClass;
}
- (NSArray *)loadPropertiesAtAddress:(uint32_t)address;
{
NSMutableArray *properties;
properties = [NSMutableArray array];
if (address != 0) {
CDDataCursor *cursor;
struct cd_objc2_list_header listHeader;
uint32_t index;
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
//NSLog(@"property list data offset: %lu", [cursor offset]);
listHeader.entsize = [cursor readInt32];
listHeader.count = [cursor readInt32];
NSParameterAssert(listHeader.entsize == 8);
for (index = 0; index < listHeader.count; index++) {
struct cd_objc2_property_32 objc2Property;
NSString *name, *attributes;
CDOCProperty *property;
objc2Property.name = [cursor readInt32];
objc2Property.attributes = [cursor readInt32];
name = [machOFile stringAtAddress:objc2Property.name];
attributes = [machOFile stringAtAddress:objc2Property.attributes];
property = [[CDOCProperty alloc] initWithName:name attributes:attributes];
[properties addObject:property];
[property release];
}
[cursor release];
}
return properties;
}
// This just gets the methods.
- (NSArray *)loadMethodsOfMetaClassAtAddress:(uint32_t)address;
{
struct cd_objc2_class_32 objc2Class;
struct cd_objc2_class_ro_t_32 objc2ClassData;
CDDataCursor *cursor;
if (address == 0)
return nil;
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
objc2Class.isa = [cursor readInt32];
objc2Class.superclass = [cursor readInt32];
objc2Class.cache = [cursor readInt32];
objc2Class.vtable = [cursor readInt32];
objc2Class.data = [cursor readInt32];
objc2Class.reserved1 = [cursor readInt32];
objc2Class.reserved2 = [cursor readInt32];
objc2Class.reserved3 = [cursor readInt32];
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Class.isa, objc2Class.superclass, objc2Class.cache, objc2Class.vtable);
//NSLog(@"%08lx %08lx %08lx %08lx", objc2Class.data, objc2Class.reserved1, objc2Class.reserved2, objc2Class.reserved3);
NSParameterAssert(objc2Class.data != 0);
[cursor setOffset:[machOFile dataOffsetForAddress:objc2Class.data]];
objc2ClassData.flags = [cursor readInt32];
objc2ClassData.instanceStart = [cursor readInt32];
objc2ClassData.instanceSize = [cursor readInt32];
//objc2ClassData.reserved = [cursor readInt32];
objc2ClassData.reserved = 0;
objc2ClassData.ivarLayout = [cursor readInt32];
objc2ClassData.name = [cursor readInt32];
objc2ClassData.baseMethods = [cursor readInt32];
objc2ClassData.baseProtocols = [cursor readInt32];
objc2ClassData.ivars = [cursor readInt32];
objc2ClassData.weakIvarLayout = [cursor readInt32];
objc2ClassData.baseProperties = [cursor readInt32];
[cursor release];
return [self loadMethodsAtAddress:objc2ClassData.baseMethods];
}
- (NSArray *)loadMethodsAtAddress:(uint32_t)address;
{
NSMutableArray *methods;
methods = [NSMutableArray array];
if (address != 0) {
CDDataCursor *cursor;
struct cd_objc2_list_header listHeader;
uint32_t index;
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
//NSLog(@"method list data offset: %lu", [cursor offset]);
listHeader.entsize = [cursor readInt32];
listHeader.count = [cursor readInt32];
NSParameterAssert(listHeader.entsize == 12);
for (index = 0; index < listHeader.count; index++) {
struct cd_objc2_method_32 objc2Method;
NSString *name, *types;
CDOCMethod *method;
objc2Method.name = [cursor readInt32];
objc2Method.types = [cursor readInt32];
objc2Method.imp = [cursor readInt32];
name = [machOFile stringAtAddress:objc2Method.name];
types = [machOFile stringAtAddress:objc2Method.types];
//NSLog(@"%3u: %08lx %08lx %08lx", index, objc2Method.name, objc2Method.types, objc2Method.imp);
//NSLog(@"name: %@", name);
//NSLog(@"types: %@", types);
method = [[CDOCMethod alloc] initWithName:name type:types imp:objc2Method.imp];
[methods addObject:method];
[method release];
}
}
return [methods reversedArray];
}
- (NSArray *)loadIvarsAtAddress:(uint32_t)address;
{
NSMutableArray *ivars;
ivars = [NSMutableArray array];
if (address != 0) {
CDDataCursor *cursor;
struct cd_objc2_list_header listHeader;
uint32_t index;
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
NSParameterAssert([cursor offset] != 0);
//NSLog(@"ivar list data offset: %lu", [cursor offset]);
listHeader.entsize = [cursor readInt32];
listHeader.count = [cursor readInt32];
NSParameterAssert(listHeader.entsize == 20);
for (index = 0; index < listHeader.count; index++) {
struct cd_objc2_ivar_32 objc2Ivar;
CDOCIvar *ivar;
objc2Ivar.offset = [cursor readInt32];
objc2Ivar.name = [cursor readInt32];
objc2Ivar.type = [cursor readInt32];
objc2Ivar.alignment = [cursor readInt32];
objc2Ivar.size = [cursor readInt32];
if (objc2Ivar.name != 0) {
NSString *name, *type;
name = [machOFile stringAtAddress:objc2Ivar.name];
type = [machOFile stringAtAddress:objc2Ivar.type];
ivar = [[CDOCIvar alloc] initWithName:name type:type offset:objc2Ivar.offset];
[ivars addObject:ivar];
[ivar release];
} else {
//NSLog(@"%08lx %08lx %08lx %08x %08x", objc2Ivar.offset, objc2Ivar.name, objc2Ivar.type, objc2Ivar.alignment, objc2Ivar.size);
}
}
}
return ivars;
}
// Returns list of uniqued protocols.
- (NSArray *)uniquedProtocolListAtAddress:(uint32_t)address;
{
NSMutableArray *protocols;
protocols = [[[NSMutableArray alloc] init] autorelease];;
if (address != 0) {
CDDataCursor *cursor;
uint32_t count, index;
cursor = [[CDDataCursor alloc] initWithData:[machOFile data]];
[cursor setByteOrder:[machOFile byteOrder]];
[cursor setOffset:[machOFile dataOffsetForAddress:address]];
count = [cursor readInt32];
for (index = 0; index < count; index++) {
uint32_t val;
CDOCProtocol *protocol, *uniqueProtocol;
val = [cursor readInt32];
if (val == 0) {
NSLog(@"Warning: protocol address in protocol list was 0.");
} else {
protocol = [protocolsByAddress objectForKey:[NSNumber numberWithUnsignedInteger:val]];
if (protocol != nil) {
uniqueProtocol = [protocolsByName objectForKey:[protocol name]];
if (uniqueProtocol != nil)
[protocols addObject:uniqueProtocol];
}
}
}
[cursor release];
}
return protocols;
}
@end