Isa analysis to metalClass
The ISA_MASK is defined by the system. Different architectures vary. I get x86_64 and emulator in objC4 source code: define ISA_MASK 0x00007ffffffffff8ULL arm64: define ISA_MASK 0x0000000ffffffff8ULL
Create a new LGPerson object instance p, and explore the ISA point relationship through LLDB dynamic debug: object – isaisaisa > class – isaisaisa > metaClass
Code:
@interface LGPerson : NSObject @end @implementation LGPerson @end int main(int argc, Const char * argv[]) {@autoreleasepool {// nonpointerIsa mask 0x00007ffFFFFFFff8 LGPerson *p = [LGPerson alloc]; NSLog(@"%@",p); // 0x0000000100008360 VS 0x0000000100008338 // LGPerson : > class ISA -> metaclass ISA -> Root metaclass ISA -> root metaclass ISA -> root metaclass ISA -> root metaclass ISA -> root metaclass ISA // root metaclass isa -> root metaclass isa // inheritance chain} return 0; }Copy the code
Breakpoint tuning attempts:
Debug printing:
(lldb) x p 0x1005b3870: 65 83 00 00 01 80 1d 01 00 00 00 00 00 00 00 00 e............... 0x1005b3880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ (lldb) p/x p (LGPerson *) $1 = 0x00000001005b3870 (lldb) x/4gx 0x00000001005b3870 0x1005b3870: 0x011d800100008365 0x0000000000000000 0x1005b3880: 0x0000000000000000 0x0000000000000000 (LLDB) 0x011D800100008365 Is isa Error for our object: '0x011d800100008365' is not a valid command. (lldb) p 0x011d800100008365 & 0x00007ffffffffff8 (long) $2 = 4295000928 (lldb) p/x 0x011d800100008365 & 0x00007ffffffffff8 (long) $3 = 0x0000000100008360 (lldb) po 0x0000000100008360 LGPerson (lldb) x/4gx 0x0000000100008360 0x100008360: 0x0000000100008338 0x00007fff808be008 0x100008370: 0x00007fff204afaa0 0x0000802c00000000 (lldb) po 0x0000000100008338 & 0x00007ffffffffff8 LGPerson (lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8 (long) $6 = 0x0000000100008338 (lldb) $6 ! = $3Copy the code
Conclusion:
p/x p
printLGPerson
Instance object p ofMemory address
x/4gx 0x00000001005b3870
Formatted print0x00000001005b3870
The data stored in the continuous address space under the address, got itLGPerson object
theThe first address
, which is the isa pointer addressp/x
0x011d800100008365
&0x00007ffffffffff8
It is resolved by the and operation of the ISA pointer and ISA_MASKLGPerson class object
theThe first address
po 0x0000000100008360
Print the address data, and you get itLGPerson
x/4gx 0x0000000100008360
Through the isa of instance object pointing to class object, I get the memory address of class object0x0000000100008360
, format outputClass object
Memory address ofp/x 0x0000000100008338 & 0x00007ffffffffff8
The first address of the class object (isa pointer address)0x0000000100008338
andISA_MASK
Do and operation, get the memory address of metaclass ·0x0000000100008338
0x0000000100008360
VS0x0000000100008338
The addresses are different, but the prints are bothLGPerson
, it isClass address
withThe metaclass address
The difference between- LGPerson: Guess that the class will open up infinite memory with our object more than one class
Verify that classes and metaclasses will open up unlimited memory
Add methods to the above code to declare multiple objects and test to see if the class address changes
Code:
Class class1 = [LGPerson Class]; void lgTestClassNum(void){Class class1 = [LGPerson Class]; Class class2 = [LGPerson alloc].class; Class class3 = object_getClass([LGPerson alloc]); Class class4 = [LGPerson alloc].class; NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4); } int main(int argc, Const char * argv[]) {@autoreleasepool {// nonpointerIsa mask 0x00007ffFFFFFFff8 LGPerson *p = [LGPerson alloc]; NSLog(@"%@",p); // 0x0000000100008360 VS 0x0000000100008338 // LGPerson : There is more than one class // 0x0000000100008338 is not a class it isa metaclass // new thing // isa walk // object isa -> class isa -> metaclass isa -> root metaclass isa -> // root metaclass isa -> root metaclass isa // inheritance chain} return 0; }Copy the code
LLDB debugging:
2021-06-23 16:52:21.495202+0800 002-ISA analysis [12154:882912] <LGPerson: 0x1005B3870 > 2021-06-23 16:52:21.495968+0800 002- ISA Analysis [12154:882912] 0x100008360-0x100008360-0x100008360-0x100008360Copy the code
Conclusion:
LGPerson
theClass object
Only one copy in memory, not the same as the object!
Rotten Apple analyzes class and metaclass memory
Further use MatchOview rotten apple analysis 002-ISA analysis. Exec
Conclusion:
The LGPerson
Class object
withThe metaclass
inexec
Only one of them
MetaClass -> rootMetaClass
From the previous section, we can see that isa for a class isa metaclass, and isa for a metaclass is the root metaclass
Debug printing:
(lldb) x/4gx p
0x10064e270: 0x011d800100008365 0x0000000000000000
0x10064e280: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x011d800100008365 & 0x00007ffffffffff8
(long) $1 = 0x0000000100008360
(lldb) x/4gx 0x0000000100008360
0x100008360: 0x0000000100008338 0x00007fff808be008
0x100008370: 0x00007fff204afaa0 0x0000802c00000000
(lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8
(long) $2 = 0x0000000100008338
(lldb) x/4gx 0x0000000100008338
0x100008338: 0x00007fff808bdfe0 0x00007fff808bdfe0
0x100008348: 0x00007fff204afaa0 0x0000e03500000000
(lldb) p/x 0x00007fff808bdfe0 & 0x00007ffffffffff8
(long) $3 = 0x00007fff808bdfe0
(lldb) po 0x00007fff808bdfe0
NSObject
(lldb)
Copy the code
The attempt to:
Conclusion:
x/4gx 0x0000000100008338
throughClass object
theIsa pointer
To find theMetaclass object address
Format output metaclass object address, getYuan class object
Is the isa pointer0x0000000100008338
p/x 0x0000000100008338 & 0x00007ffffffffff8
Again, the isa pointer address and the ISA_MASK are interacted to get an address0x00007fff808bdfe0
po 0x00007fff808bdfe0
Output address0x00007fff808bdfe0
, prints an NSObject.- Root metaclass (
rootMetaClass
) is the NSObject
The root metaclass of NSObject is
(lldb) p/x NSObject.class
(Class) $5 = 0x00007fff808be008 NSObject
(lldb) x/4gx 0x00007fff808be008
0x7fff808be008: 0x00007fff808bdfe0 0x0000000000000000
0x7fff808be018: 0x0000000100648050 0x0001801000000003
(lldb) p/x 0x00007fff808bdfe0 & 0x00007ffffffffff8
(long) $6 = 0x00007fff808bdfe0
(lldb) po 0x00007fff808bdfe0
NSObject
Copy the code
Inheritance chain
Verify superClass and ISA for custom classes and subclasses
Create a new LGTeacher class that inherits from LGPerson, and verify that the class inherits as shown in the figure:
Ttf_subclass class
theA metaclass
withSuperClass type
theA metaclass
Consensus isNSObject class
Ttf_subclass class
theThe parent class
isSuperClass type
And theSuperClass type
theThe parent class
isNSObject class
(superClass inherits from NSObject)NSObject class
Is the parent classnil
, butNSObject class
theThe metaclass
isNSObject metaclass
; whileNSObject metaclass
theThe parent class
isNSObject class
, whose root metaclass is itself!
Code:
@interface LGPerson : NSObject @end @implementation LGPerson @end @interface LGTeacher : LGPerson @end @implementation LGTeacher @end void lgTestNSObject(void){// NSObject instance object NSObject *object1 = [NSObject alloc]; // NSObject Class Class = object_getClass(object1); // NSObject Class metaClass = object_getClass(Class); // NSObject Class rootMetaClass = object_getClass(metaClass); NSObject Class rootRootMetaClass = object_getClass(rootMetaClass); NSLog (@ "\ n \ n % p % p instance objects class p metaClass \ n \ n % % p root metaClass \ n % p spikes metaClass", object1, class, metaClass, rootMetaClass, rootRootMetaClass); Class pMetaClass = object_getClass(lgperson.class); Class psuperClass = class_getSuperclass(pMetaClass); NSLog(@"LGPerson metaclass parent: %@ - %p",psuperClass,psuperClass); Class tMetaClass = object_getClass(lgteacher.class); Class tsuperClass = class_getSuperclass(tMetaClass); NSLog(@"LGTeacher metaclass parent: %@ - %p",tsuperClass,tsuperClass); // NSObject root Class special case Class nsuperClass = class_getSuperclass(nsobject.class); NSLog(@"NSObject metaclass superclass: %@ - %p",nsuperClass,nsuperClass); -> NSObject Class rnsuperClass = class_getSuperclass(metaClass); NSLog (@ "NSObject root metaclass parent class: % @ - % p", rnsuperClass, rnsuperClass); } int main(int argc, const char * argv[]) { @autoreleasepool { // 0x00007ffffffffff8 LGPerson *p = [LGPerson alloc]; NSLog(@"%@",p); // 0x0000000100008360 VS 0x0000000100008338 // LGPerson : There is more than one class // 0x0000000100008338 is not a class it isa metaclass // new thing // isa walk // object isa -> class isa -> metaclass isa -> root metaclass isa -> Root metaclass isa -> root metaclass isa // lgTestNSObject(); } return 0; }Copy the code
2021-06-25 23:07:44.993380+0800 002- ISA Analysis [33538:2348495] 0x1007042A0 Instance object 0x7FFF808Be008 Class 0x7FFF808BDFe0 Metaclath 2021-06-25 23:07:44.994396+0800 002- ISA analysis of the parent class of the metaclass [33538:2348495] LGPerson: NSObject - 0x7FFF808BDFe0 2021-06-25 23:07:44.994518+0800 002- ISA analysis of the parent class of LGTeacher [33538:2348495] : LGPerson - 0x100008338 2021-06-25 23:07:44.994579+0800 002- Isa analysis of the metaclass parent of [33538:2348495] NSObject: (null) - 0x0 2021-06-25 23:07:44.994680+0800 002- ISA analysis [33538:2348495] NSObject Root metaclase parent: NSObject - 0x7FFF808Be008 (LLDB)Copy the code
Conclusion:
NSOject
The object’sMetaclasses and root metaclasses
It’s the same class- Metaclasses also have inheritance relationships, just like classes, through printing
LGTeacher
theThe metaclass
thesuperClass
withLGPerson
theThe metaclass
They all got the same address0x100008338
- The parent class of the root metaclass points to
NSObject
, by printingNSObject
thesuperClass
With the NSObject class0x7fff808be008
address- The superclass of NSObject is (null) and the address is 0x0, which means that NSObject has no superclass
Class structure analysis
New and old versions of objc_class source code
The previous 1.0 version of objc_class
struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; #if ! __OBJC2__ Class _Nullable super_class OBJC2_UNAVAILABLE; const char * _Nonnull name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE; /* Use `Class` instead of `struct objc_class *` */Copy the code
New 2.0 objC_Class (based on objC4 (version 818) source code)
Class superclass holds the memory address of the parent Class. It takes 8 bytes of memory. Cache_t cache structure takes 16 bytes of memory. Class_data_bits_t bits manages structures that contain property, method, and so on. The memory size is determined by how many properties, methods, and so on are defined
struct objc_class : objc_object { objc_class(const objc_class&) = delete; objc_class(objc_class&&) = delete; void operator=(const objc_class&) = delete; void operator=(objc_class&&) = delete; // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; / / class_rw_t * plus custom rr/alloc flags / / below can be ignored... }Copy the code
Variable, array, structure, object memory similarities and differences
Follow up on the memory structure of class in the next section by comparing variables, arrays, structures, and objects with memory addresses and contents
Code:
#import <Foundation/Foundation.h> @interface LGPerson @end @implementation LGPerson @end #ifdef DEBUG #define LGNSLog(format, ...) Printf ("KC print: %s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]); #else #define LGNSLog(format, ...) ; #endif int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... // Int a = 10; // int b = 10; // LGNSLog(@"%d -- %p",a,&a); LGNSLog(@"%d -- %p",b,&b); // Object -lgPerson *p1 = [LGPerson alloc]; LGPerson *p2 = [LGPerson alloc]; LGNSLog(@"%@ -- %p",p1,&p1); LGNSLog(@"%@ -- %p",p2,&p2); Int c[4] = {1,2,3,4}; int *d = c; NSLog(@"%p - %p - %p",&c,&c[0],&c[1]); NSLog(@"%p - %p - %p",d,d+1,d+2); for (int i = 0; i<4; i++) { int value = *(d+i); NSLog(@"%d",value); } // OC class structure first address - shift some size -> content // lgperson. class address - shift all values NSLog(@" pointer - memory offset "); } return 0; }Copy the code
Debug printing:
KC Print: 10 -- 0x7ffeefBFF36c KC Print: 10 -- 0x7FFeEFBFF368 KC Print: <LGPerson: 0x105C040C0 > -- 0x7ffeEFBFF360 KC Print: <LGPerson: 0x105C040c0 > 0x105C040D0 > -- 0x7FFeEFBFF358 2021-06-24 16:44:03.478075+0800 002- Memory offset [23027:1912018] 0x7FFeEFBFF380-0x7FFeEFBFF380 - 0x7FFeEFBFF384 2021-06-24 16:44:03.478816+0800 002- Memory offset [23027:1912018] 0x7FFeEFB380-0x7FFeEFBFF384-0x7FfeEFBFF388 2021-06-24 16:50:34.623442+0800 002- Memory offset [23027:1912018] 1 2021-06-24 16:50:34.623577+0800 002- Memory offset [23027:1912018] 2 2021-06-24 16:50:34.623656+0800 002- Memory offset [23027:1912018] 3 2021-06-24 16:50:34.623712+0800 002- Memory offset [23027:1912018] 4 2021-06-24 16:50:34.624118+0800 002- Memory offset [23027:1912018] Pointer - Memory offset Program ended with exit code: 0Copy the code
Conclusion:
Variable: value copy object: Pointer address and content address are different Array: first address as array addressCopy the code
Class memory structure
By printing the memory contents of the class, look for the class_data_bits_t structure to learn the next properties and methods, ISA
Wrong idea
Debug breakpoints by building projects using OJC4.8! Incorrect preliminary inference about class memory distribution
Misunderstanding diagram:
Conclusion: The error is that the contents of the cache_t structure are not specifically analyzed
The right idea
Look specifically at the size of the cache_t structuresCopy the code
Methods in cache_t structures are not in memory. Static variables are in the global area and are not in their memory footprint
Private: explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //typedef unsigned long uintptr_t; Union {struct {explicit_atomic<mask_t> _maybeMask; union {struct {explicit_atomic<mask_t> _maybeMask; //typedef uint32_t mask_t; #if __LP64__ uint16_t_flags; // 1 byte #endif uint16_t _occupied; / / 2 bytes}; explicit_atomic<preopt_cache_t *> _originalPreoptCache; // The structure pointer only takes 8 bytes}; // The shared body takes just 8 bytes..... // Omit methods and staic variables because they do not occupy the memory that cache_t exploits..... }// This structure takes exactly 8+8=16 bytesCopy the code
Actually findclass_data_bits_t
Cache_t takes 16 bytes, Class ISA takes 8 bytes, and Class SuperClass takes 8 bytes. So looking for class_data_bits_t is exactly the address of the class pointer +0x20
Breakpoint debugging:
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available. (lldb) x/4gx LGPerson.class 0x100008380: 0x00000001000083a8 0x000000010036a140 0x100008390: 0x0000000100664f10 0x0002802800000003 (lldb) po 0x000000010036a140 NSObject (lldb) p/x NSObject.class (Class) $2 = 0x000000010036a140 NSObject (lldb) 0x0002802800000003 -> bit -> data error: '0x0002802800000003' is not a valid command. (lldb) x/5gx LGPerson.class 0x100008380: 0x00000001000083a8 0x000000010036a140 0x100008390: 0x0000000100664f10 0x0002802800000003 0x1000083a0: 0x0000000100664ed4 (lldb) x/6gx LGPerson.class 0x100008380: 0x00000001000083a8 0x000000010036a140 0x100008390: 0x0000000100664f10 0x0002802800000003 0x1000083a0: 0x0000000100664ed4 0x000000010036A0F0 (LLDB) Po sizeof(lgPerson. class) 8 (LLDB superclass(8bytes) + cache(16bytes) = 32 error: 'Is not a valid Command. (LLDB) Po 0x100008380+0x20 4295000992 (LLDB) p/x 0x100008380+0x20 (long) $7 = 0x00000001000083a0 (lldb) p (class_data_bits_t *)0x00000001000083a0 (class_data_bits_t *) $8 = 0x00000001000083a0 (lldb) p *$8 (class_data_bits_t) $9 = (bits = 4301672148) (lldb) p $8->data() (class_rw_t *) $10 = 0x0000000100664ed0 (lldb) p *$10 (class_rw_t) $11 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = nil nextSiblingClass = NSUUID }Copy the code
Conclusion:
0x000000010036a140
是LGPerson
thesuperClass
0x100008380+0x20
It’s the address of the LGPerson class plus 16 bytes offset, which happens to beclass_data_bits_t
Analysis of theclass_data_bits_t
The structure of the body
class_rw_t
The structure of the body
By analyzing the contents of class_data_bits_t, we can find that its main data content is the pointer to the structure that outputs class_rw_t
The source code:
struct class_data_bits_t { friend objc_class; // Values are the FAST_ flags above. uintptr_t bits; private: bool getBit(uintptr_t bit) const { return bits & bit; Class_rw_t * data() const {return (class_rw_t *)(bits & FAST_DATA_MASK); } // Some code is omitted hereCopy the code
Conclusion:
By getting data() of type class_rw_t*
The source code:
struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint16_t witness; #if SUPPORT_INDEXED_ISA uint16_t index; Explicit_atomic <uintptr_t> ro_or_rw_ext; Class firstSubclass; Class nextSiblingClass; class_rw_ext_t *deepCopy(const class_ro_t *ro) { return extAlloc(ro, true); } const method_array_t methods() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods; } else { return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()}; } } const property_array_t properties() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties; } else { return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties}; } } const protocol_array_t protocols() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols; } else { return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols}; }}};Copy the code
Conclusion:
By parsing the class_rw_T structure, you can retrieve information about the class, such as firstSubclass, methods, properties, protocols, deepCopy, ro, and so on
To obtainclass_rw_t
thefirstSubclass
Class -> class_data_bits_t -> class_rw_t -> firstSubclass
Create a new LGPerson and LGTeacher object in the project, and set a breakpoint on LGTeacher. The first breakpoint only instantiates LGPerson, and the second run instantiates LGTeacher to verify the change in firstSubclass.
Breakpoint debugging
(lldb) p $1->data() (class_rw_t *) $2 = 0x000000010070d790 (lldb) p *$2 (class_rw_t) $3 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = nil nextSiblingClass = NSUUID} // enter the second break point 2021-06-25 01:02:38.456836+0800 KCObjcBuild[25790:2082491] 0x100604d60> (lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x0000000100726800 0x0002802800000003 (lldb) p *$2 (class_rw_t) $5 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = LGTeacher nextSiblingClass = NSUUID } (lldb)Copy the code
Conclusion:
Before subclass LGTeacher has no instance, firstSubclass in class_rw_t of LGPerson is nil, which side proves that object-c is runtime. P lgteacher. class can also be changed in LLDB, because OC class is lazy loaded!
To obtainclass_rw_t
theproperties
An array of
Class -> class_data_bits_t -> class_rw_t -> property_array_t -> property_list_t -> property_t
Code:
#import <Foundation/Foundation.h> @interface LGPerson : NSObject{ NSString *subject; } @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *hobby; - (void)sayNB; + (void)say666; // 2: @end @implementation LGPerson - (instanceType)init{if (self = [super init]) {self.name = @"Cooci"; } return self; } - (void)sayNB{ } + (void)say666{ } @end @interface LGTeacher : LGPerson @property (nonatomic, copy) NSString *hobby; - (void)teacherSay; @end @implementation LGTeacher - (instanceType)init{if (self == [super init]) {NSLog(@" I'm here: %@",self); return self; } return nil; } - (void)teacherSay{ NSLog(@"%s",__func__); } @end int main(int argc, Const char * argv[] {@autoreleasepool {// LGPerson *p1 = [[LGPerson alloc] init]; LGTeacher *p2 = [[LGTeacher alloc] init]; // class_data_bits_t } return 0; }Copy the code
Breakpoint debugging:
(lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x0000000101907be0 0x0002802800000003 (lldb) p/x 0x100008388 + 0x20 (long) $1 = 0x00000001000083a8 (lldb) p (class_data_bits_t *)0x00000001000083a8 (class_data_bits_t *) $2 = 0x00000001000083a8 (lldb) p $2->data() (class_rw_t *) $3 = 0x00000001019080a0 (lldb) p $3->properties() (const property_array_t) $4 = { list_array_tt<property_t, property_list_t, RawPtr> = { = { list = { ptr = 0x0000000100008260 } arrayAndFlag = 4295000672 } } } (lldb) p $4.list (const RawPtr<property_list_t>) $5 = { ptr = 0x0000000100008260 } (lldb) p $5.ptr (property_list_t *const) $6 = 0x0000000100008260 (lldb) p *$6 (property_list_t) $7 = { entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2) } (lldb) p $7.get(0) (property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name") (lldb) p $7.get(1) (property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")Copy the code
Steps:
x/4gx LGPerson.class
Formatted outputLGPerson.class
To get the first address of the class0x100008388
p/x 0x100008388 + 0x20
Offset the first address by 32 bytes (isa
8 bytes,superclass
8 bytes,cache
16 bytes) to get the address of the class object attribute0x00000001000083a8
p (class_data_bits_t *)0x00000001000083a8
Convert the address toclass_data_bits_t
Type, for useclass_data_bits_t
The function ofp $2->data()
useclass_data_bits_t
thedata()
Function, getclass_rw_t
Address of type0x00000001019080a0
p $3->properties()
throughproperties()
Function to obtainLGPerson
The list of member variablesp $4.list
andp $5.ptr
Resolve theproperty_list_t
The address of thep *$6
Get a member variable by taking its addressproperty_list_t
p $7.get(0)
withp $7.get(1)
Gets the member variables of a class individually through a c++ functionname
,hobby
To obtainclass_rw_t
themethods
An array of
Nsobject. class -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t -> big
Class_rw_t (); class_rw_t (); class_rw_t (); class_rw_t (); There is also a layer of big() in the method_t structure, so you have to parse one more layer of structure to get the instance method. But the process found that there is no class method! So this is an instance method
The source code:
struct method_t { static const uint32_t smallMethodListFlag = 0x80000000; method_t(const method_t &other) = delete; // The representation of a "big" method. This is the traditional // representation of three pointers storing the selector, types // and implementation. struct big { SEL name; const char *types; MethodListIMP imp; }; // Omitted part of the source codeCopy the code
Comparison between method and Propetry:
Breakpoint debugging:
(lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x0000000101304120 0x0002802800000003 (lldb) p/x 0x100008388 + 0x20 (long) $1 = 0x00000001000083a8 (lldb) p (class_data_bits_t *)0x00000001000083a8 (class_data_bits_t *) $2 = 0x00000001000083a8 (lldb) p $2->data() (class_rw_t *) $3 = 0x00000001013040a0 (lldb) p $3->methods() (const method_array_t) $4 = { list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = { = { list = { ptr = 0x0000000100008160 } arrayAndFlag = 4295000416 } } } (lldb) p $4.list (const method_list_t_authed_ptr<method_list_t>) $5 = { ptr = 0x0000000100008160 } (lldb) p $5.ptr (method_list_t *const) $6 = 0x0000000100008160 (lldb) p *$6 (method_list_t) $7 = { entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6) } (lldb) p $7.get(0).big() (method_t::big) $8 = { name = "sayNB" types = 0x0000000100003f77 "v16@0:8" imp = 0x0000000100003d40 (KCObjcBuild`-[LGPerson sayNB]) } (lldb) p $7.get(1).big() (method_t::big) $9 = { name = "hobby" types = 0x0000000100003f6f "@16@0:8" imp = 0x0000000100003db0 (KCObjcBuild`-[LGPerson hobby]) } (lldb) p $7.get(2).big() (method_t::big) $10 = { name = "setHobby:" types = 0x0000000100003f8b "v24@0:8@16" imp = 0x0000000100003de0 (KCObjcBuild`-[LGPerson setHobby:]) } (lldb) p $7.get(3).big() (method_t::big) $11 = { name = "init" types = 0x0000000100003f6f "@16@0:8" imp = 0x0000000100003ce0 (KCObjcBuild`-[LGPerson init]) } (lldb) p $7.get(4).big() (method_t::big) $12 = { name = "name" types = 0x0000000100003f6f "@16@0:8" imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson name]) } (lldb) p $7.get(5).big() (method_t::big) $13 = { name = "setName:" types = 0x0000000100003f8b "v24@0:8@16" imp = 0x0000000100003d80 (KCObjcBuild`-[LGPerson setName:]) }Copy the code
Steps:
x/4gx LGPerson.class
Formatted outputLGPerson.class
To get the first address of the class0x100008388
p/x 0x100008388 + 0x20
Offset the first address by 32 bytes (ISA8 bytes, Superclass8 bytes, cache16 bytes) to get the class object property address0x00000001000083a8
p (class_data_bits_t *)0x00000001000083a8
Convert the address toclass_data_bits_t
Type, for useclass_data_bits_t
The function ofp $2->data()
useclass_data_bits_t
thedata()
Function, getclass_rw_t
Address of type0x00000001013040a0
p $3->methods()
throughmethods()
Function to obtainLGPerson
The list of instance methodsmethod_array_t
p $4.list
andp $5.ptr
Resolve themethod_list_t
The address of thep *$6
Get an array of instance variables by taking an addressmethod_list_t
.entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
, there are obviously six instance methods- Through c++ functions
get()
withbig()
To obtain a single instance of a class:p $7.get(0).big()
:-[LGPerson sayNB] Custom instance methodsayNB
p $7.get(1).big()
:-[LGPerson hobby] Member variablehobby
thegetter
Methods are generated by the systemp $7.get(2).big()
:-[LGPerson init] Custominit
methods- (instancetype)init
p $7.get(3).big()
:-[LGPerson name] Member variablename
The getter method for is generated by the systemp $7.get(0).big()
:-[LGPerson setName:] Member variablename
The setter method for psi is generated by the system
Get ivars (member variables) for class_rw_t
Nsobject. class -> class_data_bits_t -> class_rw_t -> class_ro_t -> iVAR_list_t -> iVAR_t
The source code:
struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; #endif union { const uint8_t * ivarLayout; Class nonMetaclass; }; explicit_atomic<const char *> name; // With ptrauth, this is signed if it points to a small list, but // may be unsigned if it points to a big list. void *baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; // Omitted part of the code}Copy the code
struct ivar_t { #if __x86_64__ // *offset was originally 64-bit on some x86_64 platforms. // We read and write only 32 bits of it. // Some metadata provides all 64 bits. This is harmless for unsigned // little-endian values. // Some code uses all 64 bits. class_addIvar() over-allocates the // offset for their benefit. #endif int32_t *offset; const char *name; const char *type; // alignment is sometimes -1; use alignment() instead uint32_t alignment_raw; uint32_t size; uint32_t alignment() const { if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT; return 1 << alignment_raw; }};Copy the code
LLDB dynamic debugging
(lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x000000010135c630 0x0002802800000003 (lldb) p/x 0x100008388 + 0x20 (long) $1 = 0x00000001000083a8 (lldb) p (class_data_bits_t *)$1 (class_data_bits_t *) $2 = 0x00000001000083a8 (lldb) p $2->data() (class_rw_t *) $3 = 0x000000010135c5f0 (lldb) p $3->ro() (const class_ro_t *) $4 = 0x0000000100008118 (lldb) p *$4 (const class_ro_t) $5 = { flags = 0 instanceStart = 8 instanceSize = 32 reserved = 0 = { ivarLayout = 0x0000000000000000 nonMetaclass = nil } name = { std::__1::atomic<const char *> = "LGPerson" { Value = 0x0000000100003ea8 "LGPerson" } } baseMethodList = 0x0000000100008160 baseProtocols = 0x0000000000000000 ivars = 0x00000001000081f8 weakIvarLayout = 0x0000000000000000 baseProperties = 0x0000000100008260 _swiftMetadataInitializer_NEVER_USE = {} } (lldb) p $5.ivars (const ivar_list_t *const) $6 = 0x00000001000081f8 (lldb) p *$6 (const ivar_list_t) $7 = { entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3) } (lldb) p $7.get(0) (ivar_t) $8 = { offset = 0x0000000100008320 name = 0x0000000100003f2f "subject" type = 0x0000000100003f7f "@\"NSString\"" alignment_raw = 3 size = 8 } (lldb) p $7.get(1) (ivar_t) $9 = { offset = 0x0000000100008328 name = 0x0000000100003f37 "_name" type = 0x0000000100003f7f "@\"NSString\"" alignment_raw = 3 size = 8 } (lldb) p $7.get(2) (ivar_t) $10 = { offset = 0x0000000100008330 name = 0x0000000100003f3d "_hobby" type = 0x0000000100003f7f "@\"NSString\"" alignment_raw = 3 size = 8 }Copy the code
steps
x/4gx LGPerson.class
Formatted outputLGPerson.class
To obtain the initial address0x100008388
p/x 0x100008388 + 0x20
First address offset 32 bytes (isa
8 bytes,superclass
8 bytes,cache_t
16 bytes) to get the object that contains the class attribute method member variablesclass_data_bits_t
The address of the0x00000001000083a8
p (class_data_bits_t *)$1
Translates the address toclass_data_bits_t
, in order to useclass_data_bits_t
The function ofp $2->data()
useclass_data_bits_t
thedata()
Function, getclass_rw_t
Address of type0x000000010135c5f0
p $3->ro
useclass_rw_t
thero
Function, getclass_ro_t
Address of type0x0000000100008118
p *$4
取lass_ro_t
Type the address0x0000000100008118
I got itclass_ro_t
objectp $5.ivars
useivars
Function to obtainclass_ro_t
The object’sivars
I get the pointivar_list_t
address0x00000001000081f8
A pointer to thep *$6
By taking the address0x00000001000081f8
To get an array of instance variablesivar_list_t
.entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
You can see that there are three ivars- Through c++ functions
get()
To obtain a single instance of a class:User declared member variable subject p $7.get(0): (ivar_t) $8 = { offset = 0x0000000100008320 name = 0x0000000100003f2f “subject” type = 0x0000000100003f7f “@”NSString”” alignment_raw = 3 size = 8 }
_name p $7.get(1): (ivar_t) $9 = { offset = 0x0000000100008328 name = 0x0000000100003f37 “_name” type = 0x0000000100003f7f “@”NSString”” alignment_raw = 3 size = 8 }
_hobby p $7. Get (2): (ivar_t) $10 = { offset = 0x0000000100008330 name = 0x0000000100003f3d “_hobby” type = 0x0000000100003f7f “@”NSString”” alignment_raw = 3 size = 8 }
Conclusion: IVars exists in ro, and the member variable automatically generates attributes _name,_hobby
Gets the class method for class_rw_t
We have obtained the method list of class and found that there is no class method, so the methods obtained by class class directly are instance methods, and the class methods have nowhere to go? That type of method has to be in the metaclass metalClass.
Class method acquisition process: NSObject.class -> metaClass -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t -> big
Breakpoint debugging print:
(lldb) x/4gx LGPerson.class 0x100008390: 0x00000001000083b8 0x000000010036a140 0x1000083a0: 0x0000000101470ab0 0x0002802800000003 (lldb) p/x 0x00000001000083b8 & 0x00007ffffffffff8 (long) $1 = 0x00000001000083b8 (lldb) x/4gx 0x00000001000083b8 0x1000083b8: 0x000000010036a0f0 0x000000010036a0f0 0x1000083c8: 0x0000000100731720 0x0001e03100000007 (lldb) p/x 0x1000083b8 + 0x20 (long) $2 = 0x00000001000083d8 (lldb) p (class_data_bits_t *)$2 (class_data_bits_t *) $3 = 0x00000001000083d8 (lldb) p $3->data() (class_rw_t *) $4 = 0x0000000101470a50 (lldb) p $4->methods() (const method_array_t) $5 = { list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = { = { list = { ptr = 0x00000001000082d0 } arrayAndFlag = 4295000784 } } } (lldb) p $5.list (const method_list_t_authed_ptr<method_list_t>) $6 = { ptr = 0x00000001000082d0 } (lldb) p $6.ptr (method_list_t *const) $7 = 0x00000001000082d0 (lldb) p *$7 (method_list_t) $8 = { entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1) } (lldb) p $8.get(0).big() (method_t::big) $9 = { name = "say666" types = 0x0000000100003f77 "v16@0:8" imp = 0x0000000100003e10 (KCObjcBuild`+[LGPerson say666]) } (lldb)Copy the code
Steps:
x/4gx LGPerson.class
Format print classLGPerson
To get the first address of the class0x100008390
p/x 0x00000001000083b8 & 0x00007ffffffffff8
willIsa pointer
andISA_MASK
dowith
Operation, getLGPerson
The metaclassmetaClass
x/4gx 0x00000001000083b8
, format and printLGPerson
themetaClass
To get the first address of the metaclass0x1000083b8
p/x 0x1000083b8 + 0x20
, offset the metaclass’s first address by 32 bytes (ISA
8 bytes,superclass
8 bytes,cache_t
16 bytes) of the multivariate classclass_data_bits_t
Address of the object0x00000001000083d8
p (class_data_bits_t *)0x00000001000083d8
Convert the address toclass_data_bits_t
Object for calling functionsp $3->data()
callclass_data_bits_
tdata
Function, getclass_rw_t
Address of the object0x0000000101470a50
p $4->methods()
To obtainclass_rw_t
themethods
Methods listp $5.list
andp $5.ptr
To get the pointmethod_list_t
Pointer to address0x00000001000082d0
p *$7
Get the address. Got itmethod_list_t
Object, count is 1,entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
, has a class method- Through c++ functions
get()
withbig()
Class method for obtaining a single class:p $7.get(0).big()
:(method_t::big) $9 = {
name = “say666
“
types = 0x0000000100003f77 “v16@0:8”
imp = 0x0000000100003e10 (KCObjcBuild+[LGPerson say666]
)
}