3 // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
4 // Copyright (C) 1997-2019 Steve Nygard.
8 #import <mach-o/nlist.h>
9 #import <mach-o/loader.h>
10 #import "CDMachOFile.h"
12 #import "CDLCSegment.h"
15 NSString *const ObjCClassSymbolPrefix = @"_OBJC_CLASS_$_";
17 @interface CDSymbol ()
18 @property (weak, readonly) CDMachOFile *machOFile;
23 @implementation CDSymbol
25 struct nlist_64 _nlist;
28 __weak CDMachOFile *_machOFile;
31 - (id)initWithName:(NSString *)name machOFile:(CDMachOFile *)machOFile nlist32:(struct nlist)nlist32;
33 if ((self = [super init])) {
36 _machOFile = machOFile;
37 _nlist.n_un.n_strx = 0; // We don't use it.
38 _nlist.n_type = nlist32.n_type;
39 _nlist.n_sect = nlist32.n_sect;
40 _nlist.n_desc = nlist32.n_desc;
41 _nlist.n_value = nlist32.n_value;
47 - (id)initWithName:(NSString *)name machOFile:(CDMachOFile *)machOFile nlist64:(struct nlist_64)nlist64;
49 if ((self = [super init])) {
52 _machOFile = machOFile;
53 _nlist.n_un.n_strx = 0; // We don't use it.
54 _nlist.n_type = nlist64.n_type;
55 _nlist.n_sect = nlist64.n_sect;
56 _nlist.n_desc = nlist64.n_desc;
57 _nlist.n_value = nlist64.n_value;
63 #pragma mark - Debugging
65 - (NSString *)description;
67 NSString *valueString;
70 valueString = [NSString stringWithFormat:(_is32Bit ? @"%08llx" : @"%016llx"), self.value];
72 valueString = [@" " stringByPaddingToLength:(_is32Bit ? 8 : 16) withString:@" " startingAtIndex:0];
75 return [NSString stringWithFormat:@"%@ %@ %@", valueString, [self shortTypeDescription], self.name];
80 + (NSString *)classNameFromSymbolName:(NSString *)symbolName;
82 if ([symbolName hasPrefix:ObjCClassSymbolPrefix])
83 return [symbolName substringFromIndex:[ObjCClassSymbolPrefix length]];
90 return _nlist.n_value;
93 - (CDSection *)section;
95 // We might be tempted to do [[self.machOFile segmentContainingAddress:nlist.n_value] sectionContainingAddress:nlist.n_value]
96 // but this does not work for __mh_dylib_header for example (n_value == 0, but it is in the __TEXT,__text section)
97 NSMutableArray *sections = [NSMutableArray array];
98 for (CDLCSegment *segment in self.machOFile.segments) {
99 for (CDSection *section in [segment sections])
100 [sections addObject:section];
103 // n_sect is 1-indexed (NO_SECT == 0)
104 NSUInteger sectionIndex = _nlist.n_sect - 1;
105 if (sectionIndex < [sections count])
106 return sections[sectionIndex];
111 - (CDLCDylib *)dylibLoadCommand;
113 NSUInteger libraryOrdinal = GET_LIBRARY_ORDINAL(_nlist.n_desc);
114 return [self.machOFile dylibLoadCommandForLibraryOrdinal:libraryOrdinal];
119 return (_nlist.n_type & N_EXT) == N_EXT;
122 - (BOOL)isPrivateExternal;
124 return (_nlist.n_type & N_PEXT) == N_PEXT;
129 return _nlist.n_type & N_STAB;
134 return _nlist.n_type & N_TYPE;
139 return self.type != N_UNDF;
144 return self.type == N_ABS;
149 return self.type == N_SECT;
154 return self.type == N_PBUD;
159 return self.type == N_INDR;
164 return !self.isDefined && self.isExternal && _nlist.n_value != 0;
167 - (BOOL)isInTextSection;
169 CDSection *section = self.section;
170 return [section.segmentName isEqualToString:@"__TEXT"] && [section.sectionName isEqualToString:@"__text"];
173 - (BOOL)isInDataSection;
175 CDSection *section = self.section;
176 return [section.segmentName isEqualToString:@"__DATA"] && [section.sectionName isEqualToString:@"__data"];
179 - (BOOL)isInBssSection;
181 CDSection *section = self.section;
182 return [section.segmentName isEqualToString:@"__DATA"] && [section.sectionName isEqualToString:@"__bss"];
185 - (NSUInteger)referenceType;
187 return (_nlist.n_desc & REFERENCE_TYPE);
190 - (NSString *)referenceTypeName
192 switch (self.referenceType) {
193 case REFERENCE_FLAG_UNDEFINED_NON_LAZY: return @"undefined non lazy";
194 case REFERENCE_FLAG_UNDEFINED_LAZY: return @"undefined lazy";
195 case REFERENCE_FLAG_DEFINED: return @"defined";
196 case REFERENCE_FLAG_PRIVATE_DEFINED: return @"private defined";
197 case REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: return @"private undefined non lazy";
198 case REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: return @"private undefined lazy";
203 - (NSComparisonResult)compare:(CDSymbol *)other;
205 if (other.value > self.value) return NSOrderedAscending;
206 if (other.value < self.value) return NSOrderedDescending;
208 return NSOrderedSame;
211 - (NSComparisonResult)compareByName:(CDSymbol *)other;
213 return [self.name compare:other.name];
216 - (NSString *)shortTypeDescription;
220 if (self.stab) c = @"-";
221 else if (self.isCommon) c = @"c";
222 else if (!self.isDefined || self.isPrebound) c = @"u";
223 else if (self.isAbsolute) c = @"a";
224 else if (self.isInSection) {
225 if (self.isInTextSection) c = @"t";
226 else if (self.isInDataSection) c = @"d";
227 else if (self.isInBssSection) c = @"b";
230 else if (self.isIndirect) c = @"i";
233 return self.isExternal ? [c uppercaseString] : c;
236 - (NSString *)longTypeDescription;
240 if (self.isCommon) c = @"common";
241 else if (!self.isDefined) c = @"undefined";
242 else if (self.isPrebound) c = @"prebound";
243 else if (self.isAbsolute) c = @"absolute";
244 else if (self.isInSection) {
245 CDSection *section = self.section;
246 if (section) c = [NSString stringWithFormat:@"%@,%@", section.segmentName, section.sectionName];
249 else if (self.isIndirect) c = @"indirect";