久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計實例 >> 瀏覽文章

C語言函數(shù)調(diào)用分析

作者:佚名   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時間:2014年03月13日   【字體:
我的測試環(huán)境:Fedora14
Gcc版本:gcc-4.5.1
內(nèi)核版本:2.6.38.1
 
C語言是一個強(qiáng)大的語言,特別是對于嵌入式開發(fā)過程中有時需要反匯編分析代碼中存在的問題,函數(shù)是C語言中的難點(diǎn),關(guān)于函數(shù)的調(diào)用也是很多人不能理解的,很多知道的也是一知半解。對C語言的調(diào)用有了一個比較清晰的認(rèn)識就能夠更清晰的分析代碼中存在的問題。我也是看了很多的資料,然后自己寫了一一段小代碼作為分析的測試代碼。首先記住在X86體系里很多的寄存器都有特殊的用途,其中ESP表示當(dāng)前函數(shù)堆棧的棧頂指針,而EBP則表示當(dāng)前函數(shù)堆棧的基地址。EBP是棧基址的指針,永遠(yuǎn)指向棧底(高地址),ESP是棧指針,永遠(yuǎn)指向棧頂(低地址)。 
我的代碼如下:
  1. #include<stdio.h>

  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. }
對上面的代碼進(jìn)行編譯和反匯編:
[gong@Gong-Computer deeplearn]$ gcc -g testcall.c -o testcall
[gong@Gong-Computer deeplearn]$ objdump -S -d testcall > testcall_s 
然后對反匯編的代碼進(jìn)行分析:
  1. ...
  2.  
  3. 8048393: c3 ret
  4.  
  5.  
  6.  
  7. 08048394 <pluss_a_and_b>:
  8.  
  9. #include<stdio.h>
  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 <call_plus>:
  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 <pluss_a_and_b>
  106.  
  107. }
  108.  
  109. 80483e7: c9 leave
  110.  
  111. 80483e8: c3 ret
  112.  
  113.  
  114.  
  115. 080483e9 <main>:
  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: 83 ec 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 <call_plus>
  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. ...
首先,C語言的入口都是從main函數(shù)開始的,但是從反匯編代碼中可以發(fā)現(xiàn)并不是只有自己設(shè)計的代碼,還存在很多關(guān)于初始化等操作。這主要是因為C語言的運(yùn)行需要一些基本的環(huán)境和C-RunTime的一些基本函數(shù)。因此main 函數(shù)只是我們C語言的入口,但并不是一個程序的開始。因此main函數(shù)也需要堆棧的控制,也需要壓棧出棧等操作。
需要注意的是:
指令call用來調(diào)用一個函數(shù)或過程,這時下一條指令地址被壓入堆棧中,以備返回時能恢復(fù)執(zhí)行下條指令。sp=sp-1。通過下面的匯編代碼就可知道函數(shù)的返回地址。
80483e2: e8 ad ff ff ff call 8048394 <pluss_a_and_b>
}
80483e7: c9 leave
可以知道指令call后的返回地址就是80483e7。而8048394則說明被調(diào)用函數(shù)的起始地址,這些數(shù)字可能在不同的系統(tǒng)中存在差別。
RET指令用來從一個函數(shù)或過程返回,之前CALL保存的下條指令地址會從棧內(nèi)彈出到EIP寄存器中,程序轉(zhuǎn)到CALL之前下條指令處執(zhí)行。
 
下面簡單的介紹幾個代碼:
80483e9: 55 push %ebp
80483ea: 89 e5 mov %esp,%ebp
80483ec: 83 ec 18 sub $0x18,%esp
首先push %ebp,是將調(diào)用函數(shù)的棧幀基地址壓入棧中,也就是保存調(diào)用函數(shù)的棧幀EBP。將其指向的地址壓入堆棧中。mov %esp,%ebp則是將ESP和EBP指向同一個地址,作為被調(diào)用函數(shù)的棧幀基地址。sub $0x18,%esp則是修改ESP的值,與EBP構(gòu)成當(dāng)前被調(diào)用函數(shù)的棧幀空間。
 
 
從圖中可以每個函數(shù)的棧空間都是相互獨(dú)立的,但是每一個棧空間的基本結(jié)構(gòu)都是相同的。都是該函數(shù)的EBP指針,然后是局部變量空間,然后是往下一個函數(shù)的傳遞參數(shù)空間,返回的EBP地址。這樣就能實現(xiàn)不同函數(shù)的調(diào)用,然后傳遞參數(shù)是采用基于EBP指針的相對位置實現(xiàn)的,并沒有絕對地址。
 
由此可以知道棧空間的分布是根據(jù)調(diào)用情況分析的,當(dāng)調(diào)用過多時就會導(dǎo)致溢出錯誤,因此并不是一味的迭代和遞歸。
 
關(guān)于函數(shù)調(diào)用的返回都是采用EAX寄存器實現(xiàn)的,但是當(dāng)返回的是結(jié)構(gòu)體以及聯(lián)合體時返回就不能采用EAX實現(xiàn)了,基本的實現(xiàn)方法也是基于堆棧的。
  1. #include<stdio.h>

  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. }
編譯以及反匯編以后得到如下的結(jié)果:
[gong@Gong-Computer deeplearn]$ gcc -g structpass.c -o structpass
[gong@Gong-Computer deeplearn]$ objdump -S -d structpass > structpass_s
 
  1. ...
  2.  
  3. 08048394 <my_test_of_return>:
  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 <main>:
  79.  
  80.  
  81.  
  82. int main()
  83.  
  84. {
  85.  
  86. 80483da: 8d 4c 24 04 lea 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: 89 e5 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 <my_test_of_return>
  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. ...
從上面的結(jié)果可以知道可以知道,返回的過程并不是一次通過EAX返回的,而是通過堆棧一個一個的傳遞出來,實現(xiàn)結(jié)果的返回。因此這也是我們需要注意的地方。
 
同樣對于結(jié)構(gòu)體的傳遞方式也是采用堆棧的方式進(jìn)行傳遞,基本的參看下面的分析。參數(shù)也是依據(jù)堆棧中的位置進(jìn)行控制的。
代碼:
  1. #include<stdio.h>

  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. }
編譯和反匯編過程:
[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 04 lea 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: 83 ec 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 <my_test_of_return>
  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 <my_test_pass>
  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. }
...
由上面的反匯編代碼可以知道結(jié)構(gòu)體的傳遞參數(shù)是依據(jù)堆棧實現(xiàn)的。這也說明了多參數(shù)的傳遞過程并不是按著固定的模式實現(xiàn)的,這也是我們需要注意的問題。參數(shù)的傳遞需要根據(jù)實際情況分析。
 
總結(jié):
函數(shù)的調(diào)用是有一定的方式的,各個函數(shù)都有一定的堆棧空間,而且每一個堆棧空間的分布情況也是類似的,但是大小要根據(jù)實際的情況分析。一般一個函數(shù)的堆棧空間中包含下面幾個部分:1、棧幀(用來表示該堆棧空間的棧底,也就是指開始的地址EBP),局部變量的空間,下一個被調(diào)用函數(shù)的參數(shù)傳遞,最后是返回地址(實質(zhì)上也是一個EBP)。就是依據(jù)EBP和相對位置就能知道每一個函數(shù)的基本分布,而ESP就能知道堆棧空間的大小。
 
被調(diào)用參數(shù)的獲取主要是依據(jù)EBP指針的相對位置獲得,因為被調(diào)用函數(shù)的堆棧空間上一個堆棧空間就是調(diào)用函數(shù)的堆棧空間。根據(jù)函數(shù)的棧幀指針(EBP)和相對位置(-4,-8等)找到對應(yīng)的參數(shù),但是相對位置也是不固定的,這需要考慮結(jié)構(gòu)體的對齊等方式,具體的要在實際中計算。
 
返回值一般都是采用EAX返回的,但是對于結(jié)構(gòu)體等則是采用堆棧的方式一個元算一個元素的返回的,但是還是運(yùn)用了EAX的特性。
 
函數(shù)調(diào)用的分布打開如下:

從上面的分析我們可以發(fā)現(xiàn)匯編代碼是非常有用的,建議多參看匯編代碼分析具體的問題。
關(guān)閉窗口

相關(guān)文章

主站蜘蛛池模板: 亚洲女人天堂网 | 欧美久久久久久 | av一区二区在线观看 | 毛片网在线观看 | 国产激情91久久精品导航 | 久久国产精品无码网站 | 欧美中文视频 | 在线国产小视频 | 亚洲男女激情 | 中文字幕第100页 | 日日噜噜噜夜夜爽爽狠狠视频97 | 中文字幕乱码一区二区三区 | 黑人巨大精品欧美一区二区免费 | 国产高潮av | 91视视频在线观看入口直接观看 | 亚洲一区二区三区在线播放 | 亚洲 欧美 日韩在线 | 久久中文一区二区 | 国产在线观看网站 | 视频二区| 中文字幕一区二区三区不卡 | 91黄在线观看 | 懂色中文一区二区在线播放 | 亚洲精久 | 91久久精品日日躁夜夜躁国产 | 一区二区三区亚洲精品国 | 国产在线一区二区 | 日本三级线观看 视频 | 在线观看日韩精品视频 | wwwww在线观看 | 国产91在线观看 | 91成人在线视频 | 欧美一级毛片在线播放 | 欧美一级二级在线观看 | 午夜一区二区三区在线观看 | 一区二区三区亚洲精品国 | 污片在线观看 | 91社影院在线观看 | 国产欧美精品区一区二区三区 | 黄色国产 | 在线观看精品 |