通過反匯編理解函數調用機制(x86和ARM)


如下,一個簡單的程序

 1 #include <stdio.h>
 2 int add(int a, int b)
 3 {
 4         return a + b;
 5 }
 6 
 7 void main()
 8 {
 9         int a = 1, b = 2;
10         int result;
11         result = add(a, b);
12         printf("%d",result);
13 }

執行反匯編指令:gcc -g test.c
objdump -S

得到x86機器的匯編代碼(除去一些初始化的代碼)如下:

 

 在分析上面的匯編程序之前,需要了解rbp、rsp為棧基址寄存器、棧頂寄存器,分別指向棧底和棧頂;edx、eax、esi、edi均為x86CPU上的通用寄存器,可以存放數據(雖然它們還有別的作用,但是本文章不涉及)

x86下棧生長是從高地址往低地址,即push操作一次,rsp減少4個字節,pop操作一次,rsp增加4個字節。

對上面匯編代碼的分析:

進入main函數,保護現場,將rbp壓入堆棧;

然后為main函數開拓新的堆棧框架,rbp與當前rsp相同,rsp再向上擴充16個字節(0x10);(以前的C程序只能在函數前面聲明變量,是因為編譯器還么有那么“智能”,它只能通過分析前部分的變量,一次性的為程序擴充堆棧)

然后向棧底上方的偏移地址為8和12的單元存入數據1和2;

把數據送入通用寄存器中,以供新的函數調用;

跳轉到add;

再次將main的rbp壓棧,保護;

新的rbp與當前rsp相同,把通用寄存器中的數據賦給棧底上方偏移地址為4和8的單元(此為函數參數傳遞的關鍵);

將傳入新棧的參數賦給通用寄存器,進行加法操作,結果存入eax;

pop出rbp,回到main函數;

將eax中的運算結果賦給棧底上方偏移地址為4的單元;

然后調用printf函數顯示結果。

 

使用arm-linux-gcc編譯並反匯編:arm-linux-objdump -D -m arm a.out

得到arm機器的匯編代碼(除去一些初始化的代碼)如下:

這段代碼的解析與x86類似,只不過需要了解幾個arm匯編指令和寄存器名稱。fp為幀寄存器,起“標簽”作用。lr是連接寄存器,在ARM體系結構中lr的用途有兩種:一是用來保存子程序返回地址;二是當異常發生時,lr保存的值等於異常發生時PC的值減4(或者減2),因此在各種異常模式下可以返回到異常發生前的相應位置繼續執行。bx lr即跳轉到lr存放的地址處。sp為棧頂指針。str 源寄存器 存儲地址,即將源存儲器數據送到存儲器中,ldr為其逆操作。

ARM為堆棧提供硬件支持,它有一個專門的寄存器sp指向棧頂,ARM支持四種堆棧工作方式,最常用的也是和x86類似,即從高地址向低地址生長。

參考資料:http://mooc.study.163.com/course/USTC-1000029000#/info


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM