C language function call analysis

Publisher:龙腾少年Latest update time:2015-05-06 Source: 51hei Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
My test environment: Fedora14
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:
  1. #include

  2.  
  3. int pluss_a_and_b(int a,int b)
     
  4. {
     
  5.         int c = -2;
     
  6.         return (a + b - c);
     
  7. }
     
  8. int call_plus(int *a,int *b) 
     
  9. {
     
  10.         int c = *a; 
     
  11.         int d = *b; 
     

  12.  
  13.         *a = d;
     
  14.         *b = c;
     
  15.         return pluss_a_and_b(c,d);
     
  16. }
     
  17. int main()
     
  18. {
     
  19.         int c = 10; 
     
  20.         int d = 20;
     
  21.         int g = call_plus(&c,&d);
     
  22.         return 0;
     
  23. }
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:
  1. ...
  2.  
  3. 8048393: c3 ret
  4.  
  5.  
  6.  
  7. 08048394 :
  8.  
  9. #include
  10.  
  11.  
  12.  
  13. int pluss_a_and_b(int a,int b)
  14.  
  15. {
  16.  
  17. 8048394: 55 push %ebp
  18.  
  19. 8048395: 89 e5 mov %esp,%ebp
  20.  
  21. 8048397: 83 ec 10 sub $0x10,%esp
  22.  
  23. int c = -2;
  24.  
  25. 804839a: c7 45 fc fe ff ff ff movl $0xfffffffe,-0x4(%ebp)
  26.  
  27. return (a + b - c);
  28.  
  29. 80483a1: 8b 45 0c mov 0xc(%ebp),%eax
  30.  
  31. 80483a4: 8b 55 08 mov 0x8(%ebp),%edx
  32.  
  33. 80483a7: 8d 04 02 lea (%edx,%eax,1),%eax
  34.  
  35. 80483aa: 2b 45 fc sub -0x4(%ebp),%eax
  36.  
  37. }
  38.  
  39. 80483ad: c9 leave
  40.  
  41. 80483ae: c3 ret
  42.  
  43.  
  44.  
  45. 080483af :
  46.  
  47.  
  48.  
  49. int call_plus(int *a,int *b)
  50.  
  51. {
  52.  
  53. 80483af: 55 push %ebp
  54.  
  55. 80483b0: 89 e5 mov %esp,%ebp
  56.  
  57. 80483b2: 83 ec 18 sub $0x18,%esp
  58.  
  59. int c = *a;
  60.  
  61. 80483b5: 8b 45 08 mov 0x8(%ebp),%eax
  62.  
  63. 80483b8: 8b 00 mov (%eax),%eax
  64.  
  65. 80483ba: 89 45 fc mov %eax,-0x4(%ebp)
  66.  
  67. int d = *b;
  68.  
  69. 80483bd: 8b 45 0c mov 0xc(%ebp),%eax
  70.  
  71. 80483c0: 8b 00 mov (%eax),%eax
  72.  
  73. 80483c2: 89 45 f8 mov %eax,-0x8(%ebp)
  74.  
  75.  
  76.  
  77. *a = d;
  78.  
  79. 80483c5: 8b 45 08 mov 0x8(%ebp),%eax
  80.  
  81. 80483c8: 8b 55 f8 mov -0x8(%ebp),%edx
  82.  
  83. 80483cb: 89 10 mov %edx,(%eax)
  84.  
  85. *b = c;
  86.  
  87. 80483cd: 8b 45 0c mov 0xc(%ebp),%eax
  88.  
  89. 80483d0: 8b 55 fc mov -0x4(%ebp),%edx
  90.  
  91. 80483d3: 89 10 mov %edx,(%eax)
  92.  
  93.  
  94.  
  95. return pluss_a_and_b(c,d);
  96.  
  97. 80483d5: 8b 45 f8 mov -0x8(%ebp),%eax
  98.  
  99. 80483d8: 89 44 24 04 mov %eax,0x4(%esp)
  100.  
  101. 80483dc: 8b 45 fc mov -0x4(%ebp),%eax
  102.  
  103. 80483df: 89 04 24 mov %eax,(%esp)
  104.  
  105. 80483e2: e8 ad ff ff ff call 8048394
  106.  
  107. }
  108.  
  109. 80483e7: c9 leave
  110.  
  111. 80483e8: c3 ret
  112.  
  113.  
  114.  
  115. 080483e9
    :
  116.  
  117.  
  118.  
  119. int main()
  120.  
  121. {
  122.  
  123. 80483e9: 55 push %ebp
  124.  
  125. 80483ea: 89 e5 mov %esp,%ebp
  126.  
  127. 80483ec: 83ec 18 sub $0x18,%esp
  128.  
  129. int c = 10;
  130.  
  131. 80483ef: c7 45 f8 0a 00 00 00 movl $0xa,-0x8(%ebp)
  132.  
  133.  
  134.  
  135. int d = 20;
  136.  
  137. 80483f6: c7 45 f4 14 00 00 00 movl $0x14,-0xc(%ebp)
  138.  
  139.  
  140.  
  141. int g = call_plus(&c,&d);
  142.  
  143. 80483fd: 8d 45 f4 lea -0xc(%ebp),%eax
  144.  
  145. 8048400: 89 44 24 04 mov %eax,0x4(%esp)
  146.  
  147. 8048404: 8d 45 f8 lea -0x8(%ebp),%eax
  148.  
  149. 8048407: 89 04 24 mov %eax,(%esp)
  150.  
  151. 804840a: e8 a0 ff ff ff call 80483af
  152.  
  153. 804840f: 89 45 fc mov %eax,-0x4(%ebp)
  154.  
  155.  
  156.  
  157. return 0;
  158.  
  159. 8048412: b8 00 00 00 00 mov $0x0,%eax
  160.  
  161. }
  162.  
  163. 8048417: c9 leave
  164.  
  165. 8048418: c3 ret
  166.  
  167. 8048419: 90 nop
  168.  
  169. 804841a: 90 nop
  170.  
  171. ...
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
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
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.
  1. #include

  2.  
  3. typedef struct {
     
  4.         double d;
     
  5.         float f;
     
  6.         int i;
     
  7.         char c;
     
  8. }return_value;
     

  9.  

  10.  
  11. return_value my_test_of_return()
     
  12. {
     
  13.         return_value rv; 
     
  14.     
     
  15.         rv.d = 12.56;
     
  16.         rv.f = 3.1;
     
  17.         rv.i = 10; 
     
  18.         rv.c = 'a';
     

  19.  
  20.         return rv; 
     
  21. }
     

  22.  
  23. int main()
     
  24. {
     
  25.         return_value local = my_test_of_return();

  26.  
  27.         return 0;
     
  28. }
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
 
  1. ...
  2.  
  3. 08048394 :
  4.  
  5. char c;
  6.  
  7. }return_value;
  8.  
  9.  
  10.   return_value my_test_of_return()
  11.  
  12. {
  13.  
  14. 8048394: 55 push %ebp
  15.  
  16. 8048395: 89 e5 mov %esp,%ebp
  17.  
  18. 8048397: 83 ec 20 sub $0x20,%esp
  19.  
  20. 804839a: 8b 45 08 mov 0x8(%ebp),%eax
  21.  
  22. return_value rv;
  23.  
  24.  
  25.  
  26. rv.d = 12.56;
  27.  
  28. 804839d: dd 05 d8 84 04 08 fldl 0x80484d8
  29.  
  30. 80483a3: dd 5d e8 fstpl -0x18(%ebp)
  31.  
  32. rv.f = 3.1;
  33.  
  34. 80483a6: ba 66 66 46 40 mov $0x40466666,%edx
  35.  
  36. 80483ab: 89 55 f0 mov %edx,-0x10(%ebp)
  37.  
  38. rv.i = 10;
  39.  
  40. 80483ae: c7 45 f4 0a 00 00 00 movl $0xa,-0xc(%ebp)
  41.  
  42. rv.c = 'a';
  43.  
  44. 80483b5: c6 45 f8 61 movb $0x61,-0x8(%ebp)
  45.  
  46.  
  47.  
  48. return rv;
  49.  
  50. 80483b9: 8b 55 e8 mov -0x18(%ebp),%edx
  51.  
  52. 80483bc: 89 10 mov %edx,(%eax)
  53.  
  54. 80483be: 8b 55 ec mov -0x14(%ebp),%edx
  55.  
  56. 80483c1: 89 50 04 mov %edx,0x4(%eax)
  57.  
  58. 80483c4: 8b 55 f0 mov -0x10(%ebp),%edx
  59.  
  60. 80483c7: 89 50 08 mov %edx,0x8(%eax)
  61.  
  62. 80483ca: 8b 55 f4 mov -0xc(%ebp),%edx
  63.  
  64. 80483cd: 89 50 0c mov %edx,0xc(%eax)
  65.  
  66. 80483d0: 8b 55 f8 mov -0x8(%ebp),%edx
  67.  
  68. 80483d3: 89 50 10 mov %edx,0x10(%eax)
  69.  
  70. }
  71.  
  72. 80483d6: c9 leave
  73.  
  74. 80483d7: c2 04 00 ret $0x4
  75.  
  76.  
  77.  
  78. 080483da
    :
  79.  
  80.  
  81.  
  82. int main()
  83.  
  84. {
  85.  
  86. 80483da: 8d 4c 24 04lea 0x4(%esp),%ecx
  87.  
  88. 80483de: 83 e4 f8 and $0xfffffff8,%esp
  89.  
  90. 80483e1: ff 71 fc pushl -0x4(%ecx)
  91.  
  92. 80483e4: 55 push %ebp
  93.  
  94. 80483e5: 89e5 mov %esp,%ebp
  95.  
  96. 80483e7: 51 push %ecx
  97.  
  98. 80483e8: 83 ec 2c sub $0x2c,%esp
  99.  
  100. return_value local = my_test_of_return();
  101.  
  102. 80483eb: 8d 45 e0 lea -0x20(%ebp),%eax
  103.  
  104. 80483ee: 89 04 24 mov %eax,(%esp)
  105.  
  106. 80483f1: e8 9e ff ff ff call 8048394
  107.  
  108. 80483f6: 83 ec 04 sub $0x4,%esp
  109.  
  110.  
  111.  
  112. return 0;
  113.  
  114. 80483f9: b8 00 00 00 00 mov $0x0,%eax
  115.  
  116. }
  117.  
  118. 80483fe: 8b 4d fc mov -0x4(%ebp),%ecx
  119.  
  120. 8048401: c9 leave
  121.  
  122. 8048402: 8d 61 fc lea -0x4(%ecx),%esp
  123.  
  124. ...
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:
  1. #include

  2.  
  3. typedef struct {
     
  4.         double d;
     
  5.         float f;
     
  6.         int i;
     
  7.         char c;
     
  8. }return_value;
     

  9.  
  10. return_value my_test_pass(return_value pass)
     
  11. {
     
  12.         return_value rv; 
     
  13.         rv.d = pass.d;
     
  14.         rv.f = pass.f;
     
  15.         rv.i = pass.i;
     
  16.         rv.c = pass.c;
     

  17.  
  18.         return rv; 
     
  19. }
     
  20. return_value my_test_of_return()
     
  21. {
     
  22.         return_value rv; 
     
  23.     
     
  24.         rv.d = 12.56;
     
  25.         rv.f = 3.1;
     
  26.         rv.i = 10; 
     
  27.         rv.c = 'a';
     

  28.  
  29.         return rv; 
     
  30. }
     

  31.  
  32. int main()
     
  33. {
     
  34.         return_value local = my_test_of_return();
     
  35.         return_value local1 = my_test_pass(local);

  36.  
  37.         return 0;
     
  38. }
Compilation and disassembly process:
[gong@Gong-Computer deeplearn]$ gcc -g structpass.c -o structpass
[gong@Gong-Computer deeplearn]$ objdump -S -d structpass > structpass_s
 
  1. ...
  2. int main()
  3.  
  4. {
  5.  
  6. 804841d: 8d 4c 24 04lea 0x4(%esp),%ecx
  7.  
  8. 8048421: 83 e4 f8 and $0xfffffff8,%esp
  9.  
  10. 8048424: ff 71 fc pushl -0x4(%ecx)
  11.  
  12. 8048427: 55 push %ebp
  13.  
  14. 8048428: 89 e5 mov %esp,%ebp
  15.  
  16. 804842a: 51 push %ecx
  17.  
  18. 804842b: 83ec 4c sub $0x4c,%esp
  19.  
  20. return_value local = my_test_of_return();
  21.  
  22. 804842e: 8d 45 e0 lea -0x20(%ebp),%eax
  23.  
  24. 8048431: 89 04 24 mov %eax,(%esp)
  25.  
  26. 8048434: e8 9e ff ff ff call 80483d7
  27.  
  28. 8048439: 83 ec 04 sub $0x4,%esp
  29.  
  30.  
  31.  
  32. return_value local1 = my_test_pass(local);
  33.  
  34. 804843c: 8d 45 c8 lea -0x38(%ebp),%eax
  35.  
  36. 804843f: 8b 55 e0 mov -0x20(%ebp),%edx
  37.  
  38. 8048442: 89 54 24 04 mov %edx,0x4(%esp)
  39.  
  40. 8048446: 8b 55 e4 mov -0x1c(%ebp),%edx
  41.  
  42. 8048449: 89 54 24 08 mov %edx,0x8(%esp)
  43.  
  44. 804844d: 8b 55 e8 mov -0x18(%ebp),%edx
  45.  
  46. 8048450: 89 54 24 0c mov %edx,0xc(%esp)
  47.  
  48. 8048454: 8b 55 ec mov -0x14(%ebp),%edx
  49.  
  50. 8048457: 89 54 24 10 mov %edx,0x10(%esp)
  51.  
  52. 804845b: 8b 55 f0 mov -0x10(%ebp),%edx
  53.  
  54. 804845e: 89 54 24 14 mov %edx,0x14(%esp)
  55.  
  56. 8048462: 89 04 24 mov %eax,(%esp)
  57.  
  58. 8048465: e8 2a ff ff ff call 8048394
  59.  
  60. 804846a: 83 ec 04 sub $0x4,%esp
  61.  
  62.  
  63.  
  64. return 0;
  65.  
  66. 804846d: b8 00 00 00 00 mov $0x0,%eax
  67.  
  68. }
...
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.
Reference address:C language function call analysis

Previous article:Classic Application of Macro Definition in C/C++
Next article:A Brief Discussion on UCOS Operating System Stack

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号