My test environment: Fedora14
From the above analysis, we can find that assembly code is very useful. It is recommended to refer to the assembly code to analyze specific problems.
Reference address:C language function call analysis
Gcc version: gcc-4.5.1
Kernel version: 2.6.38.1
C language is a powerful language, especially for embedded development, which sometimes requires disassembly to analyze problems in the code. Functions are the difficulty in C language, and many people cannot understand function calls, and many people who know only have a superficial understanding. Having a clearer understanding of C language calls will enable you to analyze problems in the code more clearly. I also read a lot of information, and then wrote a small piece of code as a test code for analysis. First of all, remember that many registers in the X86 system have special uses, among which ESP represents the top pointer of the current function stack, and EBP represents the base address of the current function stack. EBP is a pointer to the stack base address, which always points to the bottom of the stack (high address), and ESP is a stack pointer, which always points to the top of the stack (low address).
My code is as follows:
My code is as follows:
- #include
- int pluss_a_and_b(int a,int b)
- {
- int c = -2;
- return (a + b - c);
- }
- int call_plus(int *a,int *b)
- {
- int c = *a;
- int d = *b;
- *a = d;
- *b = c;
- return pluss_a_and_b(c,d);
- }
- int main()
- {
- int c = 10;
- int d = 20;
- int g = call_plus(&c,&d);
- return 0;
- }
Compile and disassemble the above code:
[gong@Gong-Computer deeplearn]$ gcc -g testcall.c -o testcall
[gong@Gong-Computer deeplearn]$ objdump -S -d testcall > testcall_s
Then analyze the disassembled code:
- ...
- 8048393: c3 ret
- 08048394
: - #include
- int pluss_a_and_b(int a,int b)
- {
- 8048394: 55 push %ebp
- 8048395: 89 e5 mov %esp,%ebp
- 8048397: 83 ec 10 sub $0x10,%esp
- int c = -2;
- 804839a: c7 45 fc fe ff ff ff movl $0xfffffffe,-0x4(%ebp)
- return (a + b - c);
- 80483a1: 8b 45 0c mov 0xc(%ebp),%eax
- 80483a4: 8b 55 08 mov 0x8(%ebp),%edx
- 80483a7: 8d 04 02 lea (%edx,%eax,1),%eax
- 80483aa: 2b 45 fc sub -0x4(%ebp),%eax
- }
- 80483ad: c9 leave
- 80483ae: c3 ret
- 080483af
: - int call_plus(int *a,int *b)
- {
- 80483af: 55 push %ebp
- 80483b0: 89 e5 mov %esp,%ebp
- 80483b2: 83 ec 18 sub $0x18,%esp
- int c = *a;
- 80483b5: 8b 45 08 mov 0x8(%ebp),%eax
- 80483b8: 8b 00 mov (%eax),%eax
- 80483ba: 89 45 fc mov %eax,-0x4(%ebp)
- int d = *b;
- 80483bd: 8b 45 0c mov 0xc(%ebp),%eax
- 80483c0: 8b 00 mov (%eax),%eax
- 80483c2: 89 45 f8 mov %eax,-0x8(%ebp)
- *a = d;
- 80483c5: 8b 45 08 mov 0x8(%ebp),%eax
- 80483c8: 8b 55 f8 mov -0x8(%ebp),%edx
- 80483cb: 89 10 mov %edx,(%eax)
- *b = c;
- 80483cd: 8b 45 0c mov 0xc(%ebp),%eax
- 80483d0: 8b 55 fc mov -0x4(%ebp),%edx
- 80483d3: 89 10 mov %edx,(%eax)
- return pluss_a_and_b(c,d);
- 80483d5: 8b 45 f8 mov -0x8(%ebp),%eax
- 80483d8: 89 44 24 04 mov %eax,0x4(%esp)
- 80483dc: 8b 45 fc mov -0x4(%ebp),%eax
- 80483df: 89 04 24 mov %eax,(%esp)
- 80483e2: e8 ad ff ff ff call 8048394
- }
- 80483e7: c9 leave
- 80483e8: c3 ret
- 080483e9
: - int main()
- {
- 80483e9: 55 push %ebp
- 80483ea: 89 e5 mov %esp,%ebp
- 80483ec: 83ec 18 sub $0x18,%esp
- int c = 10;
- 80483ef: c7 45 f8 0a 00 00 00 movl $0xa,-0x8(%ebp)
- int d = 20;
- 80483f6: c7 45 f4 14 00 00 00 movl $0x14,-0xc(%ebp)
- int g = call_plus(&c,&d);
- 80483fd: 8d 45 f4 lea -0xc(%ebp),%eax
- 8048400: 89 44 24 04 mov %eax,0x4(%esp)
- 8048404: 8d 45 f8 lea -0x8(%ebp),%eax
- 8048407: 89 04 24 mov %eax,(%esp)
- 804840a: e8 a0 ff ff ff call 80483af
- 804840f: 89 45 fc mov %eax,-0x4(%ebp)
- return 0;
- 8048412: b8 00 00 00 00 mov $0x0,%eax
- }
- 8048417: c9 leave
- 8048418: c3 ret
- 8048419: 90 nop
- 804841a: 90 nop
- ...
First of all, the entry of C language starts from the main function, but from the disassembled code, we can find that it is not only the code designed by ourselves, but also many operations related to initialization. This is mainly because the operation of C language requires some basic environment and some basic functions of C-RunTime. Therefore, the main function is only the entry of our C language, but not the beginning of a program. Therefore, the main function also needs stack control, and also needs operations such as stack push and pop.
It should be noted that:
The instruction call is used to call a function or procedure. At this time, the address of the next instruction is pushed into the stack so that the next instruction can be resumed when returning. sp = sp-1. The return address of the function can be known through the following assembly code.
80483e2: e8 ad ff ff ff call 8048394
}
80483e7: c9 leave
}
80483e7: c9 leave
It can be known that the return address after the call instruction is 80483e7. And 8048394 indicates the starting address of the called function. These numbers may be different in different systems.
The RET instruction is used to return from a function or procedure. The address of the next instruction saved by the previous CALL will be popped from the stack into the EIP register, and the program will jump to the next instruction before the CALL for execution.
Here are some simple codes:
80483e9: 55 push %ebp
80483ea: 89 e5 mov %esp,%ebp
80483ec: 83 ec 18 sub $0x18,%esp
80483ea: 89 e5 mov %esp,%ebp
80483ec: 83 ec 18 sub $0x18,%esp
First, push %ebp pushes the stack frame base address of the calling function into the stack, that is, saves the stack frame EBP of the calling function. Pushes the address it points to into the stack. mov %esp,%ebp points ESP and EBP to the same address as the stack frame base address of the called function. sub $0x18,%esp modifies the value of ESP, which together with EBP forms the stack frame space of the current called function.
From the diagram, we can see that the stack space of each function is independent of each other, but the basic structure of each stack space is the same. They are all the EBP pointer of the function, then the local variable space, then the parameter space for passing to the next function, and the returned EBP address. In this way, different functions can be called, and the parameter passing is realized by the relative position based on the EBP pointer, and there is no absolute address.
From this we can see that the distribution of stack space is analyzed based on the calling situation. When there are too many calls, overflow errors will occur. Therefore, it is not just iteration and recursion.
The return of a function call is implemented using the EAX register. However, when a structure or a union is returned, the return cannot be implemented using EAX. The basic implementation method is also based on the stack.
- #include
- typedef struct {
- double d;
- float f;
- int i;
- char c;
- }return_value;
- return_value my_test_of_return()
- {
- return_value rv;
-
- rv.d = 12.56;
- rv.f = 3.1;
- rv.i = 10;
- rv.c = 'a';
- return rv;
- }
- int main()
- {
- return_value local = my_test_of_return();
- return 0;
- }
After compiling and disassembling, the following results are obtained:
[gong@Gong-Computer deeplearn]$ gcc -g structpass.c -o structpass
[gong@Gong-Computer deeplearn]$ objdump -S -d structpass > structpass_s
[gong@Gong-Computer deeplearn]$ objdump -S -d structpass > structpass_s
- ...
- 08048394
: - char c;
- }return_value;
- return_value my_test_of_return()
- {
- 8048394: 55 push %ebp
- 8048395: 89 e5 mov %esp,%ebp
- 8048397: 83 ec 20 sub $0x20,%esp
- 804839a: 8b 45 08 mov 0x8(%ebp),%eax
- return_value rv;
- rv.d = 12.56;
- 804839d: dd 05 d8 84 04 08 fldl 0x80484d8
- 80483a3: dd 5d e8 fstpl -0x18(%ebp)
- rv.f = 3.1;
- 80483a6: ba 66 66 46 40 mov $0x40466666,%edx
- 80483ab: 89 55 f0 mov %edx,-0x10(%ebp)
- rv.i = 10;
- 80483ae: c7 45 f4 0a 00 00 00 movl $0xa,-0xc(%ebp)
- rv.c = 'a';
- 80483b5: c6 45 f8 61 movb $0x61,-0x8(%ebp)
- return rv;
- 80483b9: 8b 55 e8 mov -0x18(%ebp),%edx
- 80483bc: 89 10 mov %edx,(%eax)
- 80483be: 8b 55 ec mov -0x14(%ebp),%edx
- 80483c1: 89 50 04 mov %edx,0x4(%eax)
- 80483c4: 8b 55 f0 mov -0x10(%ebp),%edx
- 80483c7: 89 50 08 mov %edx,0x8(%eax)
- 80483ca: 8b 55 f4 mov -0xc(%ebp),%edx
- 80483cd: 89 50 0c mov %edx,0xc(%eax)
- 80483d0: 8b 55 f8 mov -0x8(%ebp),%edx
- 80483d3: 89 50 10 mov %edx,0x10(%eax)
- }
- 80483d6: c9 leave
- 80483d7: c2 04 00 ret $0x4
- 080483da
: - int main()
- {
- 80483da: 8d 4c 24 04lea 0x4(%esp),%ecx
- 80483de: 83 e4 f8 and $0xfffffff8,%esp
- 80483e1: ff 71 fc pushl -0x4(%ecx)
- 80483e4: 55 push %ebp
- 80483e5: 89e5 mov %esp,%ebp
- 80483e7: 51 push %ecx
- 80483e8: 83 ec 2c sub $0x2c,%esp
- return_value local = my_test_of_return();
- 80483eb: 8d 45 e0 lea -0x20(%ebp),%eax
- 80483ee: 89 04 24 mov %eax,(%esp)
- 80483f1: e8 9e ff ff ff call 8048394
- 80483f6: 83 ec 04 sub $0x4,%esp
- return 0;
- 80483f9: b8 00 00 00 00 mov $0x0,%eax
- }
- 80483fe: 8b 4d fc mov -0x4(%ebp),%ecx
- 8048401: c9 leave
- 8048402: 8d 61 fc lea -0x4(%ecx),%esp
- ...
From the above results, we can know that the return process is not returned through EAX at once, but is passed one by one through the stack to achieve the return of the result. Therefore, this is also what we need to pay attention to.
The structure is also transferred using the stack. For basic information, please refer to the following analysis. Parameters are also controlled based on their position in the stack. [page]
Code:
- #include
- typedef struct {
- double d;
- float f;
- int i;
- char c;
- }return_value;
- return_value my_test_pass(return_value pass)
- {
- return_value rv;
- rv.d = pass.d;
- rv.f = pass.f;
- rv.i = pass.i;
- rv.c = pass.c;
- return rv;
- }
- return_value my_test_of_return()
- {
- return_value rv;
-
- rv.d = 12.56;
- rv.f = 3.1;
- rv.i = 10;
- rv.c = 'a';
- return rv;
- }
- int main()
- {
- return_value local = my_test_of_return();
- return_value local1 = my_test_pass(local);
- return 0;
- }
Compilation and disassembly process:
[gong@Gong-Computer deeplearn]$ gcc -g structpass.c -o structpass
[gong@Gong-Computer deeplearn]$ objdump -S -d structpass > structpass_s
[gong@Gong-Computer deeplearn]$ objdump -S -d structpass > structpass_s
- ...
- int main()
- {
- 804841d: 8d 4c 24 04lea 0x4(%esp),%ecx
- 8048421: 83 e4 f8 and $0xfffffff8,%esp
- 8048424: ff 71 fc pushl -0x4(%ecx)
- 8048427: 55 push %ebp
- 8048428: 89 e5 mov %esp,%ebp
- 804842a: 51 push %ecx
- 804842b: 83ec 4c sub $0x4c,%esp
- return_value local = my_test_of_return();
- 804842e: 8d 45 e0 lea -0x20(%ebp),%eax
- 8048431: 89 04 24 mov %eax,(%esp)
- 8048434: e8 9e ff ff ff call 80483d7
- 8048439: 83 ec 04 sub $0x4,%esp
- return_value local1 = my_test_pass(local);
- 804843c: 8d 45 c8 lea -0x38(%ebp),%eax
- 804843f: 8b 55 e0 mov -0x20(%ebp),%edx
- 8048442: 89 54 24 04 mov %edx,0x4(%esp)
- 8048446: 8b 55 e4 mov -0x1c(%ebp),%edx
- 8048449: 89 54 24 08 mov %edx,0x8(%esp)
- 804844d: 8b 55 e8 mov -0x18(%ebp),%edx
- 8048450: 89 54 24 0c mov %edx,0xc(%esp)
- 8048454: 8b 55 ec mov -0x14(%ebp),%edx
- 8048457: 89 54 24 10 mov %edx,0x10(%esp)
- 804845b: 8b 55 f0 mov -0x10(%ebp),%edx
- 804845e: 89 54 24 14 mov %edx,0x14(%esp)
- 8048462: 89 04 24 mov %eax,(%esp)
- 8048465: e8 2a ff ff ff call 8048394
- 804846a: 83 ec 04 sub $0x4,%esp
- return 0;
- 804846d: b8 00 00 00 00 mov $0x0,%eax
- }
From the above disassembly code, we can know that the structure parameter transfer is implemented based on the stack. This also shows that the multi-parameter transfer process is not implemented according to a fixed pattern, which is also a problem we need to pay attention to. The parameter transfer needs to be analyzed according to the actual situation.
Summarize:
There is a certain way to call a function. Each function has a certain stack space, and the distribution of each stack space is similar, but the size should be analyzed according to the actual situation. Generally, the stack space of a function includes the following parts: 1. Stack frame (used to indicate the bottom of the stack space, that is, the starting address EBP), space for local variables, parameter transfer of the next called function, and finally the return address (essentially also an EBP). The basic distribution of each function can be known based on EBP and relative position, and ESP can know the size of the stack space.
The acquisition of the called function parameters is mainly based on the relative position of the EBP pointer, because the stack space of the called function is the stack space of the calling function. The corresponding parameters are found according to the stack frame pointer (EBP) and relative position (-4, -8, etc.) of the function, but the relative position is not fixed, which requires consideration of the alignment of the structure and other methods, which needs to be calculated in practice.
The return value is usually returned using EAX, but for structures, the stack is used to return one element at a time, but the characteristics of EAX are still used.
The distribution of function calls opens as follows:
From the above analysis, we can find that assembly code is very useful. It is recommended to refer to the assembly code to analyze specific problems.
Previous article:Classic Application of Macro Definition in C/C++
Next article:A Brief Discussion on UCOS Operating System Stack
- Popular Resources
- Popular amplifiers
- Multimodal perception parameterized decision making for autonomous driving
- CVPR 2023 Paper Summary: Adversarial Attack and Defense
- The C answer book solutions to the exercises in The C programming language, second edition = C Programming Language
- Embedded system development tips, a novice\'s growth log and a project manager\'s recipe
Latest Microcontroller Articles
He Limin Column
Microcontroller and Embedded Systems Bible
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
MoreSelected Circuit Diagrams
MorePopular Articles
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
MoreDaily News
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
Guess you like
- Disassemble the scanning galvanometer of the laser marking machine to see the internal circuit
- How is the microcontroller PWM to 4-20mA circuit usually implemented?
- How to configure ST-LINK/V2 in the STM32CUBEIDE environment?
- Recruiting hardware development engineers Annual salary: 120,000-300,000 | Experience: 3-8 years | Work location: Beijing, Chengdu, Wuhan
- Conformal Array for Radar Missile Seeker
- TMS570LS1224_GY30 light sensor driver
- Good information on clock division
- A question about the STM32L073 port A driver
- 【Gravity:AS7341 Review】Color Temperature Perception Measurement: Analysis of Three Chip Performance
- Problems with devices that measure temperature