Classes and structures

Variation method

We all know that classes and structures can define methods, but with one difference, value type attributes cannot be modified by their own instance methods.

If you want to modify, you can addmutatingKey words:

We can look at addingmutatingKeywords SIL file:

Don’t havemutatingKeyword method

You can see that the function has an argument by defaultPointThis is actually the structure itselfself, just like the two default arguments to methods in OCselfandcmd.

Debug_value %0: $Point,let,name "selfCopy the code
withmutatingThe keyword

And we can see that very clearly here@inout Point“, passing in a variable argument, which is essentially passing in an address

$*Point,var,name "self" we can see that self is assigning *Point to the addressCopy the code

The nature of mutant methods: For mutant methods, self passed in is marked as an inout parameter

Method dispatch

Oc uses MSG_send to dispatch methods. Swift uses msG_send to dispatch methods. Swift uses msG_send to dispatch methods.

  • mov: copy the value of one register to another register (used only between registers or between registers and constants, not memory addresses)
  • add: Adds the value of one register to the value of another register and places the result in another register
  • sub: Meets a value in one register with a value in another register and saves the result in the other register
  • and: Sets the value of one register to the value of another register in bitwise with (&) and saves the result to another register
  • orr: Sets the value of one register to the value of another register in bitwise or (|) and saves the result to another register
  • str: Writes the register value to memory
  • ldr: Reads a value from memory into a register
  • cbzAnd:0Compare, if the result is zero0Transfer (can only beat behind the command)
  • cbnz: and the0Compare, if the result is not0On transfer (ibid.)
  • cmp: Comparison instruction
  • b: Jumps to an address (no return)
  • bl: jump instruction with return, return address saved tolr(x30)
  • blr: jump instruction with return, jump to instruction followed by address saved in register (example:blr x8; Jump tox8Save the address to execute)
  • ret: a subroutine (function call) returns an instruction whose return address is saved in a register by defaultlr(x30)In the

Sub x0,x1,x2 sub x0,x1,x2 sub x0,x1,x2 sub x0,x0, x0,#0x1 ORR x0, X0,#0x1 ORR x0,x0,#0x1 ORR x0,x0,#0x1 ORR x0,#0x1 STR x0,[x0,x8] LDR X0,[x1,x2] add the values of registers X1 and x2 as the address, take the value of the memory address into register X0

So when we look at assembly code method debugging, we mainly look at bl and BLR where, as follows:

We knew that before_allocating_init()Is where the object is created,swift_releaseIt’s obvious where the object is released, so you can only see it in the middleblr x8Here is where the function call is made. We can debug it and see:

There is no doubt thatbrl x8iseatIn order to verify the whole detailed process of swift method scheduling more conveniently, we expand several more methods, as shown below:

So let’s look at the result of assembly debugging:

So let’s analyze thisx8The value derivation process, the first stepbl 0x1008d2960 ... _allocating_init()You can see that the instance object is saved inx0(The return value of the function is placed in register x0.)x0The value of is assigned tox20.ldr x8,[x20]takex20The first 8 bytes of the address, knowing that the register is 64 bits, are then placedx8So we know that these 8 bytes aremetadataOf course, we can also verify:

So now,x8It’s oursmetadataAnd then theldr x8,[x8,#0x50].metadataadd#0x50In thex8Middle, it’s actually offset#0x50, then executeblr x8Under the,eatFunction call process: findMetadata, determine the function address (metadata+ offset) to execute the function.

If we observe carefully, we can find that the addresses of the three functions are 8 bytes apart, which is exactly the size of the function pointer, so it means that the three functions are actually executed in a contiguous memory space. So we know thatSwift method scheduling is based on function table scheduling. We can also prove this result by other methods such as generating SIL files as shown below:

MAC production SIL file and open: swiftc – emit – SIL SwiftEx/main swift | xcrun swift – demangle >. / main SIL && open main. SIL ios need to bring the parameters: Swiftc-ema-sil-target x86_64-apple-ios14.2-simulator -sdk $(xcrun — show-sdK-path — SDK iphonesimulator) ViewController.swift > ViewController.sil

We saw the SIL filesil_vtable, which is the list of functions for this class. In the swift,Each class has its own table of functions. We can also see from the source code, first we restore a class through the source code:TargetClassDescriptorThis class is derived from the source code that we explored the memory structure of the class as shown below:

Then through theTargetClassMetadataThis class, findTargetClassDescriptor.So that restores this TargetClassDescriptor

struct TargetClassDescriptor {

    var flags:UInt32

    var parent:UInt32

    var name:Int32

    var accessFunctionPointer:Int32

    var fieldDescriptor:Int32

    var superClassType:Int32

    var metadataNagatvieSizeInwords:UInt32

    var metadataPositiveSizeWords:UInt32

    var numImmediateMembers:UInt32

    var numField:UInt32

    var fieldOffsetVectorOffset:UInt32

    var offset:UInt32

    var size:UInt32

//VTable(VTable)

}

Through in TargetClassDescriptor’s file search this kind of access to it an alias ClassDescriptor, in GenMeta. Found in CPP ClassContextDescriptorBuilder class, In this class we found the place to add VTable:

Here we see the function pointer added to the virtual function table. We’re guessing aboveVTableWhere it is, through the bottomMach-OFile to verify the guessing results:

Mach-o files and MachOView verify method scheduling

Mach-O is the abbreviation for Mach Object file format, which is the MAC and ios executable file format. Similar to the PE format on Windows, common. A,. Dylib, Framework, dyld,.dsym

Mach-o file format:

  • The first is the file header, which indicates that the file is in Mach-O format and specifies the target schema
  • Load Commands is a table that contains many things. The content includes the location of the region, symbol table, dynamic symbol table, etc.
The field name annotation
LC_SEGMENG_64 Maps segments (32 or 64 bits) in a file to the process address space
LC_DYLD_INFO_ONLY Dynamically link related information
LC_SYMTAB Symbolic address
LC_DYSYMTAB Dynamic symbolic address
LC_LOAD_DYLINKER Dyld load
LC_UUID File the UUID
LC_MAIN Set the main thread entry address and stack size of the program
LC_LOAD_DYLIB Path to dependent libraries, including third-party libraries
LC_FUNCTION_STARTS Function start address table
LC_CODE_SIGNATURE Code signing
  • DataThe section is mainly responsible for code and data logging.Mach-OBased onSegmentThis is a structure for organizing dataSegmentIt can contain zero or moreSection.

If we open the project executable with MachOView, we can see the following image:

Section64(_TEXT,_swift5_types)Store is swift under class, structure, enumerationdescriptorinformationHere,0000BB8CaddFFFFFB80That’s the current classdescriptorThe memory address in the Mach-o file, calculated by adding the two addresses, is0x10000B70CBut in itselfMach-OThe file also has a virtual base address, and we must use the currently calculated memory address0x10000B70CSubtract the virtual base address0x100000That is theDescriptorIn the currentDataThe offset address in the real memory of the partition, which is the final address0xB70C.

Virtual base address we are inLoad CommandsUnder theLG_SEGMENT_64(_PAGEZERO)Can be seen in. We are inSection (__TEXT __const)find0xB70CLocation of descriptor, as shown below:

So this is actually the beginningdescriptorWhat’s in it. It’s in the picture50 00 00 00So this is what descriptor starts off with, it corresponds one to oneTargetClassDescriptorA member of this structure. Then we can find the memory location of VTable by memory offset, according toTargetClassDescriptorThe member of this structure is offset by 12 4 bytes to find the position of size as shown below:

And we can see that in the picture10 00 00 00 B0 AE FF FFisVTableThe start position of theeat.run.lookThree functions.

buteatThe memory address of the function in the program running is not this, but plusASLR(random memory address) before you can get its real address in the program. Next, run the next project and check the base address of the program by command image list (listing the running module of the current program), as shown below:

The current program is running at the base address 0x0000000104454000 (because the base address always changes when the real machine runs, this is one of the new addresses captured), so we can understand that our current SwiftPhonePro project is loaded into memory at the starting address 0x0000000104454000. The current base address 0x0000000104454000 plus the memory address 0x0000B740 offset by eat in the Mach-O file is the actual memory address of the current EAT function when the program is running. The final calculated address is 0x10445F740. Here we also have to look at the structure of the function in the source code, so in the direct source code, the global search MethodDescriptor looks like this:

The address we are currently calculating0x10445F740Points to theTargetMethodDescriptorThe first address of, and then offsetMethodDescriptorFlags flags(source view 4 bytes) size, to findImpl(TargetRelativeDirectPointer<Runtime,void>), andImplThe pointer to the function is not a pointer to the function. The pointer to the function is offset (4 bytes)0x10445F740Add flags, add offset, and finally calculate the memory address of the function is0x20445A5F4:

Of course, this address is also minus the base address of the current program0x100000And, ultimately,eatThe address of the function is going to be0x10445A5F4. The final project was debugged in assembly code and printed with the same result:

So we guessed correctly that method scheduling in a class is distributed through the function table. The metadata. CPP function (initClassVTable) is used to perform the following functions:

See incomingMetadataAnd then throughself->getDescription, get the currentThe description of the metadataAnd thenVTablethroughvatableOffsetThe offset is loaded to the corresponding position, whileoffsetIs determined during the program loading process.

Structure method scheduling

Let’s look at the structure’s method scheduling, directly through assembly debugging:

It is ablGet the memory address to call, equivalent to directly determine the call after compilation, so it is static distribution. Because the structure itself has no inheritance, the current function can only be the function of the current structure, so there is no need to schedule through the function table.Structure and class extentsion are also distributed statically.

type scheduling
Value types Static distributed
class Function table distribution
NSObject subclass Function table distribution

Affects how functions are distributed

  • final: addedfinalKeyword functions cannot be rewritten, using static distribution, will not be invatableAppears in, andobjcNot visible at runtime. In practice, properties, methods, and classes do not need to be overloadedfinal.

We can see it clearly in the picture aboveThe run functionHas been optimized into direct address function calls that we generated fromSILIt’s not in the fileThe run function, as shown below:

VTableWe can only seeEat and look functions.

  • dynamic: All functions can be addeddynamicThe keyword is notobjcClass and value type functions are given dynamics, but the distribution is stillFunction table distribution.

In the example aboveeatFunction addeddynamicKey after it’s IMp points to eat1, the calleatThat’s the same thing as callingeat1Function.

  • @objc: This keyword can expose the Swift function toObjcAt runtime, the function table is still dispatched.
  • @objc + dynamic: Changes to the message distribution mode, usually@objcanddynamicUse together. If you want to call OC, you must inherit itNSObject.