在arm 匯程式設計序和c 程式之間相互呼叫時必須遵守 atpcs 規則,atpcs 規定了一些函式間呼叫的基本規則。
一、atpcs 規則
atpcs 即 arm-thumb procedure call standard(arm-thumb過程呼叫標準)的簡稱,是基於arm指令集和thumb指令集過程呼叫的規範,規定了呼叫函式如何傳遞引數,被呼叫函式如何獲取引數,以何種方式傳遞函式返回值。基本的atpcs規則包括暫存器使用規則、資料棧使用規則、引數傳遞規則。
1. 暫存器使用規則
atpcs中arm暫存器的使用規則及其名稱:
暫存器別名
使用規則
r15pc
程式計數器
r14lr
鏈結暫存器
r13sp
資料棧暫存器
r12ip
子程式內部呼叫的scratch暫存器
r11v8、fp
arm狀態區域性變數暫存器8、幀指標
r10v7、sl
arm狀態區域性變數暫存器7、棧限制
r9v6
arm狀態區域性變數暫存器6
r8v5
arm狀態區域性變數暫存器5
r7v4
arm狀態區域性變數暫存器4
r6v3
arm狀態區域性變數暫存器3
r5v2
arm狀態區域性變數暫存器2
r4v1
arm狀態區域性變數暫存器1
r3a4
引數/結果/scratch暫存器4
r2a3
引數/結果/scratch暫存器3
r1a2
引數/結果/scratch暫存器2
r0a1
引數/結果/scratch暫存器1
暫存器 r0~r15 在 atpcs 規則中的使用總結如下:
2. 資料棧使用規則
所謂資料棧的增長就是移動棧指標。當棧指標指向棧頂元素(最後乙個入棧的資料)時,稱為full棧;當棧指標指向棧頂元素(最後乙個入棧的資料)相鄰的乙個空的資料單元時,稱為empty棧。
綜合以上這兩個特點,資料棧可以分為以下4種:
縮寫名稱
指令含義
fd滿遞減
stmfd/ldmfd
堆疊通過減小儲存器的位址向下增長,堆疊指標指向內含有效資料項的最低位址
ed空遞減
stmed/ldmed
堆疊通過減小儲存器的位址向下增長,堆疊指標指向堆疊下的第乙個空位置
fa滿遞增
stmfa/ldmfa
堆疊通過增大儲存器的位址向上增長,堆疊指標指向內含有效資料項的最高位址
ea空遞增
stmea/ldmea
堆疊通過增大村吃器的位址向上增長,堆疊指標指向堆疊上的第乙個空位置
atpcs規定資料棧為fd型別,並且對資料棧的操作是8位元組對齊的。使用s***b/ldmia批量記憶體訪問指令來操作fd資料棧。
使用s***b命令往資料棧中儲存內容時,"先遞減sp指標,再儲存資料";
使用ldmia命令從資料棧中恢復資料時,"先獲得資料,再遞增sp指標";
sp指標總是指向棧頂元素,這剛好是fd棧的定義。
3. 引數傳遞規則
一般來說,當引數個數不超過4個時,使用r0~r3這4個暫存器來傳遞引數;如果引數個數超過4個,剩餘的引數通過資料棧來傳遞。
二、匯程式設計序如何向 c 程式的函式傳遞引數
三、c 程式如何返回結果給匯程式設計序
四、c 函式為何要用棧
總的來說,棧的作用就是: 儲存現場/上下文,傳遞引數。
1. 儲存現場/上下文
儲存現場,也叫儲存上下文。
現場,相當於案發現場,總有一些現場的情況,要記錄下來的,否則被別人破壞掉之後,你就無法恢復現場了。而此處說的現場,就是指 cpu 執行的時候,用到了一些暫存器,比如 r0~r3,lr 等等,對於這些暫存器的值,如果你不儲存而直接跳轉到函式中去執行,那麼很可能會被破壞了,因為函式執行需要用到這些暫存器。
因此在函式呼叫之前,應該將這些暫存器等現場,暫時儲存起來,等呼叫函式執行完畢返回後,再恢復現場,這樣 cpu 就可以正確的繼續執行了。
儲存暫存器的值,一般用的是 push 指令,將對應的某些暫存器的值,乙個個放到棧中,即所謂的入棧。
然後待被呼叫的子函式執行完畢的時候,再呼叫 pop,把棧中的乙個個的值,賦值給對應的入棧的暫存器,即所謂的出棧。
2. 傳遞引數
當函式被呼叫並且引數大於 4 個時,(不包括第 4 個引數)第 4 個引數後面的引數就儲存在棧中。
五、分析乙個例項
匯程式設計序:
.text.global _start_start: ldr sp, =4096 /* 呼叫main */ bl mainhalt: b halt
c程式:
int main()
將上面程式進行反彙編:
disassembly of section .text:00000000 <_start>: 0: e3a0da01 mov sp, #4096 ; 0x1000 4: eb000000 bl c 00000008 : 8: eafffffe b 8 0000000c : c: e1a0c00d mov ip, sp 10: e92dd800 s***b sp!, 14: e24cb004 sub fp, ip, #4 ; 0x4 18: e24dd008 sub sp, sp, #8 ; 0x8 1c: e3a03456 mov r3, #1442840576 ; 0x56000000 20: e2833050 add r3, r3, #80 ; 0x50 24: e50b3010 str r3, [fp, #-16] 28: e3a03456 mov r3, #1442840576 ; 0x56000000 2c: e2833054 add r3, r3, #84 ; 0x54 30: e50b3014 str r3, [fp, #-20] 34: e51b2010 ldr r2, [fp, #-16] 38: e3a03c01 mov r3, #256 ; 0x100 3c: e5823000 str r3, [r2] 40: e51b2014 ldr r2, [fp, #-20] 44: e3a03000 mov r3, #0 ; 0x0 48: e5823000 str r3, [r2] 4c: e3a03000 mov r3, #0 ; 0x0 50: e1a00003 mov r0, r3 54: e24bd00c sub sp, fp, #12 ; 0xc 58: e89da800 ldmia sp,
簡單分析下上面的反彙編:
mov sp, #4096:設定棧位址在4k ram的最高處,sp=4096;bl c :調到c位址處的main函式,並儲存下一行**位址到lr,即lr=8;mov ip, sp:給ip賦值sp的值,ip=sp=4096s***b sp!, :按高編號暫存器存在高位址,依次將pc、lr、ip、fp存入sp-4中;sub fp, ip, #4:fp的值為ip-4=4096-4=4092;sub sp, sp, #8:sp的值為sp-8=(4096-4x4)-8=4072;mov r3, #1442840576:r3賦值0x5600 0000; add r3, r3, #80:r3的值加0x50,即r3=0x5600 0050;str r3, [fp, #-16]:r3存入[fp-16]所在的位址,即位址4076處存放0x5600 0050;mov r3, #1442840576:r3賦值0x5600 0000; add r3, r3, #84:r3的值加0x54,即r3=0x5600 0054;str r3, [fp, #-20]:r3存入[fp-20]所在的位址,即位址4072處存放0x5600 0054;ldr r2, [fp, #-16]:r2取[fp-16]位址處的值,即[4076]位址的值,r2=0x5600 0050;mov r3, #256:r3賦值為0x100;str r3, [r2]:將r3寫到r2內容所對應的位址,即0x5600 0050位址處的值為0x100;;對應c語言*pgpfcon = 0x100;;ldr r2, [fp, #-20]:r2取[fp-20]位址處的值,即[4072]位址的值,r2=0x5600 0054;mov r3, #0:r3賦值為0x00;str r3, [r2]:將r3寫到r2內容所對應的位址,即0x5600 0054位址處的值為0x00;對應c語言*pgpfdat = 0;mov r3, #0:r3賦值為0x00;mov r0, r3:r0=r3=0x00;sub sp, fp, #12:sp=fp-12=4092-12=4080;ldmia sp, :從棧中恢復暫存器,fp=4080位址處的值=原來的fp,sp=4084位址處的值=4096,pc=4088位址處的值=8,隨後調到0x08位址處繼續執行。
記憶體資料情況:
匯程式設計序呼叫c程式
首先是匯程式設計序,還是前面的例子,只是加了2行程式 extern main 說明這個函式從外面程式獲得 section data charact db a section text global start start mov ecx,charact push ecx call usestack ...
匯程式設計序 呼叫C庫函式
當我們在vs環境下學習組合語言的時候,呼叫win32的api函式進行控制台的輸入和輸出是非常麻煩的。但是c的庫函式怎麼呼叫又好像不清楚,以下是在vs2017環境下呼叫c庫函式printf和scanf函式實現控制台輸入輸出.includelib kernel32.lib 這個庫用於呼叫win32的ex...
ARM彙編程式設計之C程式呼叫匯程式設計序
編寫乙個彙編子程式,實現兩個字資料的加法運算,編寫乙個c程式來呼叫該彙編子程式,並將運算結果使用printf 函式顯示出來。示例如下 為解決這個問題,分別編寫滿足需求的c程式和匯程式設計序。c程式源 define uint32 unsigned int extern uint32 add uint3...