Recently, I have seen many analyses about uboot, and to run C language, it is necessary to prepare the stack.
In the Uboot’s start.S assembly code, regarding system initialization, I also saw the action of initializing the stack pointer. However, I have only seen people say that system initialization requires initializing the stack, that is, correctly assigning a value to the stack pointer sp, but I have never seen anyone explain why the stack needs to be initialized.
Today, we will attempt to explain why the stack needs to be initialized, namely:
Why does C language function calls require a stack while assembly does not need to initialize the stack?
To understand this question, we must first understand the role of the stack.
A detailed explanation of the role of the stack would take a long time, so here I will provide a brief introduction.
In general, the role of the stack is: to save the context, to pass parameters.
1. Saving Context
The context refers to the situation at the scene, which needs to be recorded; otherwise, if it is destroyed by others, you will not be able to restore it. Here, the context refers to the registers used by the CPU during operation, such as r0, r1, etc. If you do not save the values of these registers and directly jump to a child function to execute, they may be destroyed since the function execution also needs to use these registers.
Therefore, before calling a function, these registers and other contexts should be temporarily saved, and after the called function has finished executing and returned, the context should be restored. This way, the CPU can continue executing correctly.
In computers, you often see the term context. The corresponding English word is context. So:
What is Context?
Saving context is also called saving the context.
The context, in English, is called context, which refers to the content related to the current CPU operation, namely the registers you are using. Therefore, it is the same as the above-mentioned context.
To save the values of the registers, the push instruction is generally used to place the values of the corresponding registers into the stack, which is called pushing onto the stack.
Then, when the called sub-function has completed execution, the pop instruction is used to assign the values from the stack back to the corresponding registers that were used when you first pushed them onto the stack, which is called popping from the stack.
Among the saved registers, the value of lr is also included (because when jumping with the bl instruction, the previous value of pc is stored in lr), and when the subroutine has finished executing, the value of lr from the stack is popped out and assigned to pc, thus achieving the correct return of the sub-function.
2. Passing Parameters
When C language calls a function, it often passes some parameters to the called function. For these C language level parameters, when compiled into assembly language by the compiler, a place must be found to store them and make the called function accessible; otherwise, parameter passing cannot be achieved. There are two situations for finding a place to store them.
One situation is that the parameters being passed are very few, and can be passed through registers.
Since in the previous action of saving the context, the corresponding register values have already been saved, at this moment, these registers are free and available for use, so parameters can be placed there. In the case of few parameters, it is sufficient to store them, for example, if there are 2 parameters, then r0 and r1 can be used to store them. (Regarding which parameter goes into r0 and which goes into r1, it is related to the APCS’s rules on “passing/returning parameters between function calls”; APCS has detailed conventions. Interested readers can study it themselves.)
However, if there are too many parameters and there are not enough registers, then the excess parameters must be pushed onto the stack.
That is, the stack can be used to pass all those excess parameters that cannot fit into the registers.
3. Example Analysis of How C Language Function Calls Use the Stack
The explanation above about the role of the stack seems a bit abstract; here I will use an example to illustrate it simply, making it easier to understand:
Use:
1. arm-inux-objdump –d u-boot > dump_u-boot.txt
This will generate the dump_u-boot.txt file. This file contains the executable assembly code of the program in u-boot, where we can see which assembly code corresponds to the source code of the C language function.
Below are the assembly codes of two functions, one is clock_init,the other is another function CopyCode2Ram in the same C source file:
1. 33d0091c
2. 33d0091c: e92d4070 push {r4, r5, r6, lr}
3. 33d00920: e1a06000 mov r6, r0
4. 33d00924: e1a05001 mov r5, r1
5. 33d00928: e1a04002 mov r4, r2
6. 33d0092c: ebffffef bl 33d008f0
7. … …
8. 33d00984: ebffff14 bl 33d005dc
9. … …
10. 33d009a8: e3a00000 mov r0, #0 ; 0x0
11. 33d009ac: e8bd8070 pop {r4, r5, r6, pc}
12.
13. 33d009b0
14. 33d009b0: e3a02313 mov r2, #1275068416 ; 0x4c000000
15. 33d009b4: e3a03005 mov r3, #5 ; 0x5
16. 33d009b8: e5823014 str r3, [r2, #20]
17. … …
18. 33d009f8: e1a0f00e mov pc, lr
(1) Code of clock_init
We can see that the first line of this function:
1. 33d009b0: e3a02313 mov r2, #1275068416 ; 0x4c000000
does not have the expected push instruction, and does not push any register values onto the stack. This is because the registers used in this part of clock_init, such as r2, r3, etc., do not conflict with the registers r0 used before calling clock_init. Therefore, there is no need to use push to save these registers. However, one register to note is r14, which is lr, as it was used when calling clock_init with the bl instruction, so it automatically assigns the value of pc during the jump to lr, thus there is no need to use the push instruction to save the value of PC onto the stack.
The last line of clock_init code:
1. 33d009f8: e1a0f00e mov pc, lr
is the common mov pc, lr, which assigns the value of lr, namely the previously saved PC value during the function call, to the current PC, thus achieving the correct return of the function, that is, returning to the next instruction position during the function call.
This way, the CPU can continue executing the remaining part of the code originally in the function.
(2) Code of CopyCode2Ram
1. 33d0091c: e92d4070 push {r4, r5, r6, lr}
is what we expect, using the push instruction to save r4, r5, r6, and lr. The use of push to save r4, r5, and r6 is because of the so-called saving the context, which will be restored later when the function returns, and using push to save lr is because this function also calls other functions:
1. 33d0092c: ebffffef bl 33d008f0
2. … …
3. 33d00984: ebffff14 bl 33d005dc
4. … …
These also use the bl instruction, which will change the lr value when we first entered clock_init, so we need to use push to temporarily save it. Correspondingly, the last line of CopyCode2Ram:
1. 33d009ac: e8bd8070 pop {r4, r5, r6, pc}
is where we pop the previously pushed values back to the corresponding registers, where the last one assigns the original pushed lr value to PC, thus achieving the function return. Additionally, we note that the second-to-last line in CopyCode2Ram is:
1. 33d009a8: e3a00000 mov r0, #0 ; 0x0
which assigns 0 to the r0 register, which is how we pass the return value, through the r0 register.
The return value here is 0, corresponding to the “return 0” in the C language source code.
Regarding which register to use to pass the return value:
Of course, you can also use other temporarily free registers to pass the return value, but these processing methods are designed based on the conventions of register usage in ARM’s APCS; you should not change the usage arbitrarily. It is better to follow the conventions to ensure that the program is more standardized.
1. Electronic magazine, why not get a copy!
2.Will FPGA replace DSP? Don’t ask this anymore…
3.Li Kaifu says there are 5 fields that can make money, and 2 industries to avoid!
4.How to thrive in the electronics industry? (About MCU, ARM, DSP, Embedded)
5.Still confused about which programming language to start with? Let’s talk about JavaScript first
6.Whole country resists THAAD, but can’t compete with Samsung’s tricks!
This article is a network reprint, copyright belongs to the original author. If there are copyright issues, please contact us, and we will confirm the copyright based on the materials you provide and pay fees or delete the content.