Introduction to assembly language


Go to: Introduction to Assembly Language Tutorial


To learn assembly language, you must first understand two knowledge points: registers and memory models.

Let's look at the registers first. The CPU itself is only responsible for calculations, not for storing data. Data is generally stored in memory, and the CPU goes to the memory to read and write data when it needs to be used. However, the computing speed of the CPU is much higher than the reading and writing speed of the memory. In order to avoid being slowed down, the CPUs have their own first-level cache and second-level cache. Basically, the CPU cache can be seen as a memory with faster read and write speeds.

However, the CPU cache is still not fast enough. In addition, the address of the data in the cache is not fixed, and the CPU must address each time it is read and written, which will slow down the speed. Therefore, in addition to the cache, the CPU also has its own register (register) to store the most commonly used data. In other words, the most frequently read and write data (such as loop variables) will be placed in the register, the CPU will give priority to read and write the register, and then the register will exchange data with the memory.


Registers do not rely on addresses to distinguish data, but on names. Each register has its own name. We tell the CPU which register to get the data from, so the speed is the fastest. Some people liken the register as the zero-level cache of the CPU.

Fourth, the types of registers

Early x86 CPUs had only 8 registers, and each had a different purpose. There are now more than 100 registers, all of which have become general-purpose registers and are not specifically designated for use, but the names of early registers have been preserved.

EAX

EBX

ECX

EDX

I KNOW

ESI

EBP

ESP

Among the above 8 registers, the first seven are all common. The ESP register has a specific purpose to save the address of the current Stack.

We often see names like 32-bit CPU and 64-bit CPU, which actually refer to the size of registers. The register size of a 32-bit CPU is 4 bytes.

Five, memory model: Heap

Registers can only store a small amount of data. Most of the time, the CPU has to direct the registers to exchange data directly with the memory. Therefore, in addition to registers, you must also understand how the memory stores data.

When the program is running, the operating system will allocate a section of memory to it to store the program and the data generated by the operation. This section of memory has a start address and an end address, for example, from 0x1000 to 0x8000, the start address is the smaller address, and the end address is the larger address.

During the running of the program, for dynamic memory occupancy requests (such as creating a new object, or using the malloc command), the system will allocate a portion of the pre-allocated memory to the user. The specific rule is to start from the starting address Division (actually, the starting address will have a piece of static data, which is ignored here). For example, if the user asks for 10 bytes of memory, he will be allocated from the starting address 0x1000 and continue to the address 0x100A. If 22 bytes are required, then it will be allocated to 0x1020.

This memory area divided by the user's active request is called Heap. It starts from the start address and grows from the low bit (address) to the high bit (address). An important feature of Heap is that it will not disappear automatically. It must be manually released or recycled by the garbage collection mechanism.

Sixth, memory model: Stack

Except for Heap, other memory usage is called Stack. Simply put, Stack is the memory area temporarily occupied by the function running.

Please see the example below.

intmain(){int a=2;int b=3;}

In the above code, when the system starts to execute the main function, it will create a frame in the memory for it, and all main internal variables (such as a and b) are stored in this frame. After the execution of the main function is over, the frame will be recycled, release all internal variables, and no longer occupy space.

What happens if other functions are called inside the function?

intmain(){int a=2;int b=3;returnadd_a_and_b(a,b);}

In the above code, the add_a_and_b function is called inside the main function. When this line is executed, the system will also create a new frame for add_a_and_b to store its internal variables. In other words, there are two frames at the same time: main and add_a_and_b. Generally speaking, there are as many frames as there are in the call stack.

When add_a_and_b finishes running, its frame will be recycled, and the system will return to the place where the function main interrupted execution just now, and continue execution. Through this mechanism, layer-by-layer function calls are realized, and each layer can use its own local variables.

All frames are stored in Stack. Because frames are superimposed layer by layer, Stack is called stack. Generate a new frame, called "push into the stack", English is push; stack recovery is called "pop", English is pop. The feature of Stack is that the latest frame that is pushed into the stack is the first to be popped out of the stack (because the innermost function is called, the operation ends first), which is called a "last in, first out" data structure. Each time the function execution ends, a frame is automatically released, and all functions execution ends, and the entire Stack is released.

Stack starts from the end address of the memory area and allocates from the high order (address) to the low order (address). For example, the end address of the memory area is 0x8000, the first frame is assumed to be 16 bytes, then the next allocated address will start from 0x7FF0; the second frame assumes that 64 bytes are needed, then the address will move to 0x7FB0.

Seven, CPU instructions

7.1 An example

After understanding the register and memory model, you can see what assembly language is. Below is a simple program example.c.

intadd_a_and_b(int a,int b){returna+b;}intmain(){returnadd_a_and_b(2,3);}

gcc converts this program into assembly language.

$ gcc-S example.c

After the above command is executed, a text file example.s will be generated, which is assembly language and contains dozens of lines of instructions. Let's put it this way, for a simple operation of a high-level language, the bottom layer may consist of several or even dozens of CPU instructions. The CPU executes these instructions in sequence to complete this step.

After simplifying example.s, it looks like the following.

_add_a_and_b:push%ebx mov%eax,[%esp+8]mov%ebx,[%esp+12]add%eax,%ebx pop%ebx ret _main:push3push2call _add_a_and_b add%esp,8ret

It can be seen that the two functions add_a_and_b and main of the original program correspond to the two tags _add_a_and_b and _main. Inside each label is the CPU running process converted by the function.

Each line is an operation performed by the CPU. It is divided into two parts, just take one of them as an example.

push%ebx

In this line, push is the CPU instruction, and %ebx is the operator used by the instruction. A CPU instruction can have zero or more operators.

Next, I will explain the assembler line by line. It is recommended that readers copy this program in another window, so as not to scroll up the page when reading.

7.2 push command

According to the convention, the program starts execution from the _main tag. At this time, a frame is created for main on the Stack, and the address pointed to by the Stack is written into the ESP register. If there is data to be written into the main frame later, it will be written to the address saved in the ESP register.

Then, the first line of code is executed.

push3

The push instruction is used to put the operator into the Stack, here is to write 3 into the main frame.

Although it looks simple, the push instruction actually has a pre-operation. It will first take the address in the ESP register, subtract 4 bytes from it, and then write the new address into the ESP register. Subtraction is used because Stack develops from high to low, and 4 bytes are because the type of 3 is int, which occupies 4 bytes. After getting the new address, 3 will be written into the four bytes at the beginning of this address.

push2

The second line is the same, the push instruction writes 2 into the main frame, the position is close to the previously written 3. At this time, the ESP register will be subtracted by 4 bytes (accumulated minus 8).

7.3 call instruction

The call instruction on the third line is used to call the function.

call _add_a_and_b

The above code means to call the add_a_and_b function. At this time, the program will look for the _add_a_and_b tag and create a new frame for the function.

The code of _add_a_and_b will be executed below.

push%ebx

This line indicates that the value in the EBX register is written into the _add_a_and_b frame. This is because if you want to use this register later, you first take out the value in it, and then write it back after using it.

At this time, the push instruction will subtract 4 bytes from the address in the ESP register (accumulatively minus 12).

7.4 mov instruction

The mov instruction is used to write a value to a register.

mov%eax,[%esp+8]

This line of code means that first add 8 bytes to the address in the ESP register to get a new address, and then fetch data from the Stack according to this address. According to the previous steps, it can be deduced that what is taken out here is 2, and then 2 is written into the EAX register.

The next line of code does the same thing.

mov%ebx,[%esp+12]

The above code adds 12 bytes to the value of the ESP register, and then fetches the data from the Stack according to this address. This time, it fetches 3 and writes it to the EBX register.

7.5 add command

The add instruction is used to add two operators and write the result to the first operator.

add%eax,%ebx

The above code adds the value of the EAX register (ie 2) to the value of the EBX register (ie 3) to get the result 5, and then writes this result into the first operator EAX register.

7.6 pop command

The pop instruction is used to fetch the last value written in Stack (ie the value of the lowest address) and write this value to the location specified by the operator.

pop%ebx

The above code means to take out the value recently written by Stack (ie the original value of the EBX register), and then write this value back to the EBX register (because the addition has already been done, the EBX register is not used).

Note that the pop instruction will also add 4 to the address in the ESP register, that is, 4 bytes will be recovered.

7.7 ret instruction

The ret instruction is used to terminate the execution of the current function and return the running right to the upper function. That is, the frame of the current function will be recycled.

right

As you can see, this instruction has no operators.

As the add_a_and_b function terminates execution, the system returns to the place where the main function was interrupted just now, and continues execution.

add%esp,8

The above code means that manually add 8 bytes to the address in the ESP register and write it back to the ESP register. This is because the ESP register is the write start address of the Stack. The previous pop operation has already recovered 4 bytes. Here, 8 bytes are recovered, which is equal to all recovery.

right

Finally, the main function ends, and the ret instruction exits the program execution.

Guess you like

Origin blog.csdn.net/qq_36171263/article/details/96834171