一步步编写操作系统 65 标准调用约定stdcall 汇编实战
因?yàn)閏語言遵循的調(diào)用約定是cdecl,咱們也自然要遵守cdecl約定了。不過為了起到對(duì)比的作用,除了介紹cdecl外,也會(huì)介紹下stdcall。
既然咱們用的是調(diào)用約定是cdecl,那對(duì)它的介紹最好讓它離下一節(jié)的內(nèi)容近一些,所以先說一下咱們不用的stdcall吧^_^,其實(shí)這兩個(gè)差別就在于由誰來回收棧空間。
stdcall的調(diào)用約定意味著:
這兩點(diǎn)在上文的表中大家已有所了解,下面咱們將理論實(shí)踐一下,還是拿上面說過的函數(shù)舉例:
1 int subtract(int a, int b); //被調(diào)用者 2 int sub = subtract (3,2); //主調(diào)用者第1行是個(gè)函數(shù)聲明,其實(shí)現(xiàn)已經(jīng)在前面看到了,就是“return a-b”。
第2行進(jìn)行函數(shù)調(diào)用,實(shí)參分別是3和2。在實(shí)際調(diào)用中,參數(shù)按照從右向左的順序,參數(shù)b會(huì)先被壓入棧,然后是參數(shù)a壓入棧。在stdcall調(diào)用約定下,這個(gè)c代碼被編譯后的匯編語句是:
主調(diào)用者:
; 從右到左將參數(shù)入棧 1 push 2 ;壓入?yún)?shù)b 2 push 3 ;壓入?yún)?shù)a 3 call subtract ;調(diào)用函數(shù)subtract以上是主調(diào)函數(shù),現(xiàn)在看下被調(diào)函數(shù)subtract中做了什么。
被調(diào)用者:
1 push ebp ;壓入ebp備份。 2 mov ebp,esp ;將esp賦值給ebp, ;用ebp做為基址來訪問棧中參數(shù)。 3 mov eax,[ebp+0x8] ;偏移8字節(jié)處為第1個(gè)參數(shù)a。 4 add eax,[ebp+0xc] ;偏移0xc字節(jié)處是第2個(gè)參數(shù)b, ;參數(shù)a和b相加后存入eax。 5 mov esp,ebp ;為防止中間有入棧操作,用ebp恢復(fù)esp。 ;本句在此例子中可有可無,屬于通用代碼。 6 pop ebp ;將ebp恢復(fù) 7 ret 8 ;數(shù)字8表示返回后使esp+8。;函數(shù)返回時(shí)由被調(diào)函數(shù)清理了棧中參數(shù)。當(dāng)執(zhí)行流進(jìn)入到subtract后,在它的內(nèi)部為了用ebp做為基址引用棧中參數(shù),先執(zhí)行了push ebp來備份ebp,再將棧指針賦給了ebp。目前棧中布局如圖
?
大家根據(jù)上圖很容易地看出ebp偏移為8字節(jié)是參數(shù)a,偏移12字節(jié)是參數(shù)b。以上代碼值得說一下的是ret 8這句。stdcall是被調(diào)用者負(fù)責(zé)清理?xiàng)?臻g,這里的被調(diào)用者是函數(shù)subtract。也就是說,subtract需要在返回前或返回時(shí)完成。在返回前清理?xiàng)O鄬?duì)困難一些,清理?xiàng)J侵笇m敾赝说絽?shù)之前。因?yàn)榉祷氐刂吩趨?shù)之下,ret指令執(zhí)行時(shí)必須保證當(dāng)前棧頂是返回地址。所以通常在返回時(shí)“順便”完成。于是ret指令便有了這樣的變體,其格式為:
ret 16位立即數(shù)
這是允許在返回時(shí)順便再將棧指針esp修改的指令。順便說一句,由于32位下push指令不是壓入字就是壓入雙字,所以ret的參數(shù)必須是偶數(shù)。在ret 8執(zhí)行之前,當(dāng)前棧頂必須是返回地址,即使沒有第5行的代碼,當(dāng)前esp也是等同于ebp,因?yàn)橹皼]有任何push壓棧操作,這是編譯器為了通用性而加進(jìn)去的,所以我們?cè)谧⑨屩袑懙?#xff0c;此句可有可無。在經(jīng)過第6行將棧頂(當(dāng)前esp指向的內(nèi)存)彈出到ebp之后,ebp被恢復(fù),此時(shí)esp指向了+4字節(jié)的位置。即當(dāng)前棧頂為主調(diào)函數(shù)的返回地址。結(jié)合上圖,ret指令將棧頂?shù)臄?shù)據(jù)彈出到寄存器eip后,棧指針esp自加4,由于還有個(gè)參數(shù)8,所以esp又被加了8,從而跳過了參數(shù)a和b,順利地完成了被調(diào)用者清理?xiàng)5娜蝿?wù)。
stdcall是調(diào)用者在棧中壓入?yún)?shù),由被調(diào)用者回收棧空間。貌似分工很明確,配合很默契。因?yàn)楸徽{(diào)用者知道自己需要幾個(gè)參數(shù),所以知道要回收多少棧空間。 但轉(zhuǎn)念一想,凡事都要自己親力親為才放心,調(diào)用者壓入的參數(shù),萬一被調(diào)用者忘記回收棧空間該怎么辦(這一點(diǎn)由高級(jí)語言編譯器保證,一般不會(huì),大伙兒放心,本段這么寫是為了表述下一種調(diào)用約定方式的特點(diǎn)),參數(shù)多了棧會(huì)溢出的。下面咱們就要介紹這種“親力親為”的調(diào)用約定,即:調(diào)用者自己向棧中壓入?yún)?shù),還是由調(diào)用者自己回收棧空間。
好啦,stdcall調(diào)用約定就到此為止。大爺再來玩哦。
總結(jié)
以上是生活随笔為你收集整理的一步步编写操作系统 65 标准调用约定stdcall 汇编实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 便宜3.3万元!苹果M2性能有多强?挑落
- 下一篇: 售价20熬夜水人参成本仅2元:爆火背后是