程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 由一個vc內嵌asm的BUG引出的...

由一個vc內嵌asm的BUG引出的...

編輯:vc教程

  vc中內嵌asm時的bug

  在語法上, 我們通常認為以下的兩條語句是等價的:

mov ecx, offset DATA_LABLE     //其中DATA_LABLE是數據定義標簽
lea ecx, DATA_LABLE

  而更進一步, 我們也會認為以下兩句是等價的:

mov ecx, ebp-8
lea ecx, [ebp-8]

  第一種, 用的是存儲器尋址方式; 而第二種, 用的是寄存器尋址和寄存器間接尋址方式. 讓我意想不到的是, 在第二種情況下, vc的處理並沒有讓寄存器尋址和寄存器間接尋址方式的mov和lea兩者之間實現等價. 在使用 _asm{} 的方式將"mov ecx, ebp-8"這條語句括起來編譯之後, 很遺憾地, 我在vc的反匯編窗口發現它變成了這樣的一條語句: "mov ecx, ebp". 啊哦, 我的"-8"竟然不翼而飛了! 到目前為止, 我尚沒有查到造成這種現象的原因所在, 我只能暫時將它歸為vc的bug了.

  對gcc下會不會存在這個問題呢? 為更進一步證實, 我使用gcc重新寫了這句代碼: "mov ecx, ebp-8", 但重寫後的代碼由當初的一句變成了這樣的兩句:

movl %ebp, %ecx
subl $8, %ecx

  之所以改寫成這樣的兩句, 是因為我發現在AT&T的匯編語法中, 對於雙寄存器尋址的操作, 不能對寄存器取的值作任何變換, 也就是說不能寫成"movl %ebp-8, %ecx"的形式, 而寄存器間接尋址的操作就可以作變換, 比如:

movl -8(%ebp), %ecx             此句相當於intel asm裡的:   mov ecx, [ebp-8]
movl (%ebp, %eax), %ecx         此句相當於intel asm裡的:   mov ecx, [ebp+eax]
movl (%ebp, %eax, 4), %ecx      此句相當於intel asm裡的:   mov ecx, [ebp+eax*4]
movl -8(%ebp, %eax, 4), %ecx    此句相當於intel asm裡的:   mov ecx, [ebp+eax*4-8]

  從以上幾條語句來看, 似乎AT&T語法對寄存器間接尋址方式的支持沒有intel asm更具人性化, 但我猜想AT&T之所以采用這樣的方法, 可能一定程度上也是為了提高微指令級的執行效率.

當然, "mov ecx, ebp-8"這句也可以改寫成這樣的兩句:
subl $8, %ebp
movl %ebp, %ecx

  但一般不會這麼作, 道理是很顯然的, ebp通常會作為函數內的基址寄存器, 用於存放函數入口點的堆棧首地址, 這個值的改變會直接影響其後語句對局部變量以及函數參數的引用發生變化, 所以, 在函數首部之後的執行體中, ebp通常是不允許被改變的, 這也是我們設計自己的匯編代碼時所應該遵循的原則.

  不知道vc為什麼會將ebp之後的立即數作丟棄處理, 這顯然是沒有道德的行為. 這讓我想起了這樣的一句話: 不要試圖幫助用戶去糾正錯誤, 而是當錯誤發生時去提醒用戶, 因為程序再聰明也不會始終明白設計者的真正意圖 ,我們所需要作的就是"為異常捕獲提供盡可能詳細的日志, 並及時通知用戶這種異常, 試圖糾正異常的作法從方法上就是錯誤和愚蠢的".

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved