blob: 9a672226d01a3118544a676d56041dc56446d6f6 [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-2019 Steve Nygard.
#import "CDObjectiveCProcessor.h"
#import "CDClassDump.h"
#import "CDMachOFile.h"
#import "CDVisitor.h"
#import "CDLCSegment.h"
#import "CDLCDynamicSymbolTable.h"
#import "CDLCSymbolTable.h"
#import "CDOCProtocol.h"
#import "CDTypeController.h"
#import "CDOCClass.h"
#import "CDOCCategory.h"
#import "CDSection.h"
#import "CDProtocolUniquer.h"
// Note: sizeof(long long) == 8 on both 32-bit and 64-bit. sizeof(uint64_t) == 8. So use [NSNumber numberWithUnsignedLongLong:].
@implementation CDObjectiveCProcessor
{
CDMachOFile *_machOFile;
NSMutableArray *_classes;
NSMutableDictionary *_classesByAddress;
NSMutableArray *_categories;
CDProtocolUniquer *_protocolUniquer;
}
- (id)initWithMachOFile:(CDMachOFile *)machOFile;
{
if ((self = [super init])) {
_machOFile = machOFile;
_classes = [[NSMutableArray alloc] init];
_classesByAddress = [[NSMutableDictionary alloc] init];
_categories = [[NSMutableArray alloc] init];
_protocolUniquer = [[CDProtocolUniquer alloc] init];
}
return self;
}
#pragma mark - Debugging
- (NSString *)description;
{
return [NSString stringWithFormat:@"<%@:%p> machOFile: %@",
NSStringFromClass([self class]), self,
self.machOFile.filename];
}
#pragma mark -
- (BOOL)hasObjectiveCData;
{
return self.machOFile.hasObjectiveC1Data || self.machOFile.hasObjectiveC2Data;
}
- (CDSection *)objcImageInfoSection;
{
// Implement in subclasses.
return nil;
}
- (NSString *)garbageCollectionStatus;
{
if (self.objcImageInfoSection != nil) {
// The SDK frameworks (i.e. within Xcode, not in /System) have empty sections.
if (self.objcImageInfoSection.size < 8)
return @"Unknown";
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithSection:self.objcImageInfoSection];
[cursor readInt32];
uint32_t v2 = [cursor readInt32];
//NSLog(@"%s: %08x %08x", __cmd, v1, v2);
// v2 == 0 -> Objective-C Garbage Collection: Unsupported
// v2 == 2 -> Supported
// v2 == 6 -> Required
//NSParameterAssert(v2 == 0 || v2 == 2 || v2 == 6);
// See markgc.c in the objc4 project
switch (v2 & 0x06) {
case 0: return @"Unsupported";
case 2: return @"Supported";
case 6: return @"Required";
}
return [NSString stringWithFormat:@"Unknown (0x%08x)", v2];
}
return nil;
}
#pragma mark -
- (void)addClass:(CDOCClass *)aClass withAddress:(uint64_t)address;
{
[_classes addObject:aClass];
[_classesByAddress setObject:aClass forKey:[NSNumber numberWithUnsignedLongLong:address]];
}
- (CDOCClass *)classWithAddress:(uint64_t)address;
{
return [_classesByAddress objectForKey:[NSNumber numberWithUnsignedLongLong:address]];
}
- (void)addClassesFromArray:(NSArray *)array;
{
if (array != nil)
[_classes addObjectsFromArray:array];
}
- (void)addCategoriesFromArray:(NSArray *)array;
{
if (array != nil)
[_categories addObjectsFromArray:array];
}
- (void)addCategory:(CDOCCategory *)category;
{
if (category != nil)
[_categories addObject:category];
}
#pragma mark - Processing
- (void)process;
{
if (self.machOFile.isEncrypted == NO && self.machOFile.canDecryptAllSegments) {
[self.machOFile.symbolTable loadSymbols];
[self.machOFile.dynamicSymbolTable loadSymbols];
[self loadProtocols];
[self.protocolUniquer createUniquedProtocols];
// Load classes before categories, so we can get a dictionary of classes by address.
[self loadClasses];
[self loadCategories];
}
}
- (void)loadProtocols;
{
// Implement in subclasses.
}
- (void)loadClasses;
{
// Implement in subclasses.
}
- (void)loadCategories;
{
// Implement in subclasses.
}
- (void)registerTypesWithObject:(CDTypeController *)typeController phase:(NSUInteger)phase;
{
for (CDOCClass *aClass in _classes)
[aClass registerTypesWithObject:typeController phase:phase];
for (CDOCCategory *category in _categories)
[category registerTypesWithObject:typeController phase:phase];
for (CDOCProtocol *protocol in [self.protocolUniquer uniqueProtocolsSortedByName])
[protocol registerTypesWithObject:typeController phase:phase];
}
- (void)recursivelyVisit:(CDVisitor *)visitor;
{
NSMutableArray *classesAndCategories = [[NSMutableArray alloc] init];
[classesAndCategories addObjectsFromArray:_classes];
[classesAndCategories addObjectsFromArray:_categories];
[visitor willVisitObjectiveCProcessor:self];
[visitor visitObjectiveCProcessor:self];
// TODO: Sort protocols by dependency
// TODO: (2004-01-30) It looks like protocols might be defined in more than one file. i.e. NSObject.
// TODO: (2004-02-02) Looks like we need to record the order the protocols were encountered, or just always sort protocols
for (CDOCProtocol *protocol in [self.protocolUniquer uniqueProtocolsSortedByName])
[protocol recursivelyVisit:visitor];
if ([[visitor classDump] shouldSortClassesByInheritance]) {
[classesAndCategories sortTopologically];
} else if ([[visitor classDump] shouldSortClasses])
[classesAndCategories sortUsingSelector:@selector(ascendingCompareByName:)];
for (id aClassOrCategory in classesAndCategories)
[aClassOrCategory recursivelyVisit:visitor];
[visitor didVisitObjectiveCProcessor:self];
}
// Returns list of NSNumber containing the protocol addresses
- (NSArray *)protocolAddressListAtAddress:(uint64_t)address;
{
// Implement in subclasses
return nil;
}
@end