Last review

  • Summary of assembly
    • A programming language that uses mnemonics in place of machine instructions
    • Assembly and machine instructions one – to – one correspondence, get binary can disassemble
    • Because assembly and CPU instruction sets correspond, assembly is not portable
  • Bus: A collection of wires
    • Address bus: The wider the address bus, the better the addressing capability
    • Data bus: The width determines the throughput of CPU data
    • Control bus
  • Into the system
    • Any base is composed of the corresponding number of symbols, symbols can be customized
    • 2/8/16 is a relatively perfect base and the relationship between them is
      • Three binary numbers use a base 8 identifier
      • Four binary numbers use a hexadecimal identifier
      • Two hexadecimal bits can identify a byte
    • Number of units
      • 1024=1k; 1024k=1M; 1024M=1G
      • B: Byte (byte) 1B=8 bits
      • Bit :1 binary bit
    • Width of data
      • Data in a computer has a width, beyond which it will overflow
  • Register: The CPU creates a small temporary storage area internally for performance purposes
    • Floating point vector register
    • Abnormal status register
    • Universal register: In addition to storing data, it sometimes has a special purpose
      • ARM64 has 32 64-bit general purpose registers X0-X30 and XZR (zero register)
      • For 32-bit compatibility, ARM64 has w0-W28 \WZR30 32-bit registers
      • 32-bit registers do not exist independently, such as w0 being the lower 32 bits of X0
    • PC register: Instruction pointer register
      • The value in the PC register holds the address of the instruction that the CPU needs to execute next
      • Changing the value of the PC can change the flow of the program
      • Instructions executed by the CPU must be pointed to by the PC register

The stack

A stack is a storage space with special access (Last In First Out, Last In First Out,LIFO)

Q: The example at the end of last class went into an infinite loop, so does an infinite loop necessarily cause a crash?

In case one, each loop will stretch the stack and cause an OOM (Out Of Memory) crash when the stack meets the heap

Note right now is called a stack overflow, no single stack overflow and the stack overflow, heap from low to high addresses, stack space extend from high to low addresses, system allocated certain virtual space to each process, when the system memory tension or process quickly use up their own virtual space at a certain strategy when they decided to kill the process

.text .global _A _A: sub sp,sp,#0x20 stp x0,x1,[sp,#0x10] ldp x1,x0,[sp,#0x10] bl _A add sp,sp,#0x20 ; Stack balance ret _Copy the code

In case two, each time the loop is stack balanced, it will continue to execute without crashing

.text .global _A _A: sub sp,sp,#0x20 stp x0,x1,[sp,#0x10] ldp x1,x0,[sp,#0x10] add sp,sp,#0x20 ; Stack balance BL _A RET _Copy the code

SP and FP registers

  • The SP register holds our address at the top of the stack at any given time
  • The FP register, also known as the X29 register, is used to hold the address at the bottom of the stack when functions are nested

Starting with ARM64, 32-bit LDM, STM, PUSH, and POP instructions are removed in favor of LDR, LDP, STR, and STP. In ARM64, stack operations are 16-byte aligned

Function call stack

The stack address is opened from the high address to the bottom address, so to open the address is to subsub SP pointer, reclaim the stack space is to add SP pointer,

sub sp,sp,#0x40 ; STP x29,x30,[sp,#0x30]; Add x29,x29,#0x30; The X20 (FP) register points to the bottom of the stack... ldp x29,x30,[sp,#0x30] ; Add sp,sp,#0x40; Stack balance retCopy the code

Read and write instructions about memory

Note that data is read and written to higher addresses. For example, if 32 bytes of space are allocated but 16 bytes of data are stored, the 16 bytes of higher addresses are stored first

STR (store register) instructions

To read data out of a register and store it in memory

LDR (load register) instructions

To read data out of memory and store it in registers, this variant of LDR and STR LDP and STP can operate on two registers at the same time, for example, IF I want to store the value of register X0 into stack space, I can write it like this

sub sp,sp,#0x10 str x0,[sp] ; LDR x0,[sp]; Restore the stack value pointed to by sp to the x0 register add sp,sp,#0x10Copy the code

If I want to swap registers x0 and x1, I can write it like this

sub sp,sp,#0x20 stp x0,x1,[sp,#0x10] ; LDP x1,x0,[sp,#0x10]; Add sp,sp,#0x10 to the x1 and x0 registersCopy the code
  • STR \ STP and LDR \ LDP are instructions specifically used to manipulate registers and memory
  • After we get the SP pointer, we first stretch the stack space, and then manipulate the stack space

practice

Let’s create a new project and create a new file named ASM.s

.text
.global _A

_A:
    sub sp,sp,#0x20
    mov x0,#0xaaaa
    mov x1,#0xbbbb
    stp x0,x1,[sp,#0x10]
    ldp x1,x0,[sp,#0x10]
    add sp,sp,#0x20
    ret
Copy the code

We step through it, now we are about to stretch the stack space, at this point sp = 0x000000016AF3dc50

Sp = 0x000000016AF3DC30 after stretching stack space by 32 bytes

Debug -> Debug Workflow -> View Memory

Continuing the step, we can see that registers X0 and x1 are assigned

Step forward to STP x0, x1, [sp, #0x10] and you can see that the values of registers X0 and x1 have been stored in stack space

Step again and see that the values of registers X0 and X1 are swapped

The stack balancing

You can see that the value on the stack is still there, and when the stack balances it becomes garbage, and the next time you stretch the stack it overwrites the value in memory

Bl and RET instructions

Bl instruction

  • Place the address of the next instruction into the LR (X30) register
  • Go to the label

Bl has two meanings, one is to modify lr (x30) value, the other is to jump

Ret instruction

  • The default value of the LR (X30) register is used, and the underlying instruction prompts the CPU to use this as the address of the next instruction

ARM64 platform feature instructions, it is optimized for hardware processing

Bl instructions and RET instructions appear in pairs. When bl instructions are encountered, LR stores the address of the next instruction until ret instructions are encountered, and the instruction execution in the LR register will be triggered

practice

.text
.global _A,_B


_A:
    sub sp,sp,#0x20
    mov x0,#0xa
    mov x1,#0xb
    bl _B
    add sp,sp,#0x20
    ret


_B:
    mov x0,#0xb
    mov x1,#0xa
    ret
Copy the code

We see that the LR register and the PC register store the same address value until the BL instruction is encountered

When encountering bl instruction, the value of the LR register does not change until encountering the next BL instruction or RET instruction, and the value of the PC register still points to the address of the instruction to be executed

After the bl instruction is encountered again, the value of LR changes, saving the return_AAddress of function

Triggers the instruction stored in the LR register when ret instruction is encountered

When ret instruction is encountered again, the instruction stored in the LR register will still trigger, and the problem arises when LR jumps to_BThe function later saves the return_AThe address of the function, but no record is returnedViewDidLoadThe address of the function, thus creating an infinite loop

Have been circulating

That’s what caused the endless loops in the last video

Save the way home (LR register)

Function jump relation is ViewDidLoad -> _A -> _B

  • ViewDidLoad -> _AWhen the LR register saves the returnViewDidLoadAddress of function
  • _A -> _BWhen the LR register saves the return_AAddress of function
  • _A <- _BWhen the lr register can normally be saved by the instruction back to_Afunction
  • ViewDidLoad <- _AAt this point, the LR register still stores the return_AThe address of the function, thus creating an infinite loop

The solution is to save the path home (lr register value) when the function is nested, so can we save it in another register?? This is not possible, no one is sure whether the register will be used in future calls, so we should save the LR register in the stack space of the current function as a local variable, and we can modify it this way

.text .global _A,_B _A: sub sp,sp,#0x20 stp x29,x30,[sp,#0x10] ; Mov x0,# 0xA mov x1,#0xb BL _B LDP x29,x30,[sp,#0x10]; Add sp,sp,#0x20 ret _B: mov x0,#0xb mov x1,#0xa ret add sp,sp,#0x20 ret _B: mov x0,#0xb mov x1,#0xa retCopy the code

At this point, LR stores the return_AThe instruction address of the function

At that moment, from_AFunction restores the value of the LR register in the stack

After the RET directive is encountered

Can return normallyViewDidLoadFunction to solve the dead-loop problem left over from the previous section 🎉

These two instructions can be optimized into one line

sub sp,sp,#0x10 ; Spanning stack space STP X29, X30,[SP]; Save the x29,x30 valuesCopy the code
stp x29,x30,[sp,#-0x10]! ; Stretch stack space and assign valuesCopy the code

Similarly, the following two sentences can be optimized into one line of instructions

ldp x29,x30,[sp,#0x10] ; Add sp,sp,#0x10; The stack balancingCopy the code
ldp x29,x30,[sp],#0x10 ; Restore x29,x30 values and restore stack balanceCopy the code

Again, operations on the stack are 16 byte aligned, so keep that in mind

sub sp,sp,0x8 str x0,[sp] ldr x0,[sp] ; This is the problem add sp,sp,0x8Copy the code

Lr and PC summary

Through the above practice we know that when there is no encounter bl instructions lr register save registers and PC is the execute instruction address, but met bl instructions lr register values no longer change, until meet ret instructions or another bl will change, lr register can be understood as a nested function call to return to the path of the first-class functions, The PC register simply points to the next instruction to be executed. When a function is only nested at one level, we do not need to operate on the LR register, but when a function is nested at multiple levels, we need to manually save the VALUE of the LR register, otherwise it will cause an infinite loop

Function with arguments

It doesn’t matter if you can’t write it, but let’s write a higher-level function and see how the system generates it, okay

The parameters are first saved in registers W0,w1

Add the values of registers w0 and w1 to the stack, then read the values of registers w8 and w9 from the stack, add the values of registers W8 and w9 to the stack, and save the results of the function to w0

So we can implement a function that takes arguments like this

.text
.global _A


_A:
    add x0,x0,x1
    ret
Copy the code

Execution result no problem scatter flower applause 🎉👏

Under ARM64, function parameters are stored in the x0-x7 (w0-w7) register. If more than 8 parameters are stored in the x0 register, function return values are also stored in the stack space if 8 bytes are not enough

For efficiency purposes, it is best to write an OC code with no more than six arguments, since the function itself has two invisible arguments, self and selector. If more than six arguments must be used, it is best to use array or structure Pointers