What is the embedded C language function call stack?
Function calls are implemented through the stack, and it is known that local variables of the function are stored on the stack. But the implementation details of the stack may not be clear. This article will introduce how the function stack is implemented on the Linux platform. Understanding the deep principles of the system is very helpful for analyzing difficult problems.
Just as familiarity with packet capture is a high-level weapon for solving network communication problems, familiarity with the function call stack is an advanced weapon for analyzing program memory problems. This article takes the development of C language under Linux 64-bit operating system as an example, introduces the implementation principle of application call stack, and analyzes the call stack content of a program through an instance and GDB tool. Before introducing a specific call stack, let's introduce some basic knowledge, which is the basis for understanding the subsequent function call stack.
Intel's CPUs are forward-compatible when designed, that is, they can run compiled programs on older generation CPUs on a new generation of CPUs. To ensure compatibility, the new generation CPU retains the alias of the older generation registers. Taking the 16-bit register AX as an example, AL indicates the lower 8 bits and AH indicates the upper 8 bits. After the 32-bit CPU was introduced, the 32-bit register was represented by a register named EAX, and AX was retained. By analogy, RAX represents a 64-bit register.
In order to understand the details of the function call stack, it is necessary to understand the implementation of the function call in the assembler. The function call is mainly divided into two parts, one is the call and the other is the return. In assembly language, the function call is completed by the call instruction, and the return is by the ret instruction.
The assembly language call instruction is equivalent to performing a 2-step operation, respectively, 1) pushing the current IP or CS and IP onto the stack; 2) jumping, similar to the jmp instruction. Similarly, the ret instruction is also divided into 2 steps, respectively, 1) popping the address in the stack to the IP register; 2) jumping to execute subsequent instructions. This is basically the principle of function calls.
In addition to the beating between the code, the function call often needs to pass a parameter, and there may be a return value after the processing is completed. The transfer of these data is done through registers. The function is stored in the register described above before the function call, and the result is stored back through the RAX register (32-bit system for EAX) before the function returns.
Another important knowledge point is the stack-related registers RSP and RBP in the function call process. The two registers mainly record the stack position. The specific functions are as follows: RSP: reextended stack pointer, which is stored in the file. A pointer that always points to the top of the stack of the top stack of the system stack. RBP: Reextended base pointer, which holds a pointer that always points to the bottom of the stack frame at the top of the system stack.
The name of the register is related to the architecture. This article is a 64-bit system, so the registers are RSP and RBP. In the case of a 32-bit system, the names of the registers are ESP and EBP.
Let's take a look at the main contents of the function call stack as a whole. In the function stack, it mainly includes the function parameter table, the local variable table, the base address of the stack, and the function return address. The base address of the stack here is the base address of the previous stack frame, because the base address is needed to access the contents of the stack in this function, so the base address in the previous stack frame needs to be pushed first.
For the sake of understanding, we take a specific program as an example. This program is very simple, mainly to simulate the function call relationship and