跳轉到內容

x86 反彙編/迴圈

來自華夏公益教科書,自由的教科書,為自由的世界

為了完成重複的任務,程式設計師通常會實現迴圈。迴圈有很多種類,但它們在彙編程式碼中都可以簡化為幾個相似的格式。本章將討論迴圈,如何識別它們,以及如何將它們“反編譯”回高階表示。

Do-While 迴圈

[編輯 | 編輯原始碼]

看起來這部分會首先考慮Do-While迴圈,這似乎違反直覺,因為在實踐中,它們可能是所有變體中最少使用的。但是,我們的瘋狂是有方法的,所以請繼續閱讀。

考慮以下通用的 Do-While 迴圈

 do
 {
    action;
 } while(condition);

這個迴圈做什麼?迴圈體簡單地執行,條件在迴圈結束時進行測試,如果條件滿足,則迴圈跳轉回迴圈的開頭。與if語句不同,Do-While條件不會反轉。

現在讓我們看一下以下 C 程式碼

 do
 {
   x++;
 } while(x != 10);

它可以被翻譯成這樣的組合語言

 mov eax, $x
 beginning:
 inc eax
 cmp eax, 0x0A ;0x0A = 10
 jne beginning
 mov $x, eax

While 迴圈

[編輯 | 編輯原始碼]

While迴圈看起來幾乎和Do-While迴圈一樣簡單,但實際上它們並不簡單。讓我們檢查一個通用的 while 迴圈

 while(x)
 {
    //loop body
 }

這個迴圈做什麼?首先,迴圈檢查以確保 x 為真。如果 x 不為真,則跳過迴圈。然後執行迴圈體,然後進行另一次檢查:x 仍然為真嗎?如果 x 仍然為真,執行跳轉回迴圈頂部,繼續執行。請記住,在迴圈底部需要有一個跳轉(返回迴圈頂部),但在條件被發現為假的情況下,跳回頂部、重新測試條件,然後再跳回迴圈底部是沒有意義的。while 迴圈然後執行以下步驟

  1. 檢查條件。如果為假,則轉到末尾
  2. 執行迴圈體
  3. 檢查條件,如果為真,則跳轉到步驟 2。
  4. 如果條件不為真,則從迴圈末尾穿透。

以下是在 C 程式碼中的 while 迴圈

 while(x <= 10)
 {
    x++;
 }

下面是同一個迴圈翻譯成彙編程式碼

 mov eax, $x
 cmp eax, 0x0A
 jg end
 beginning:
 inc eax
 cmp eax, 0x0A
 jle beginning
 end:

如果我們將該彙編程式碼反翻譯回 C,我們將得到以下程式碼

 if(x <= 10) //remember: in If statements, we reverse the condition from the asm
 {
   do
   {
     x++;
   } while(x <= 10)
 }

看到我們為什麼先介紹 Do-While 迴圈了嗎?因為當 While 迴圈被彙編時,它變成了 Do-While。

那麼為什麼跳轉標籤不能出現在測試之前呢?

mov eax, $x
beginning:
cmp eax, 0x0A
jg end
inc eax
jmp beginning
end:
mov $x, eax

For 迴圈

[編輯 | 編輯原始碼]

什麼是 For 迴圈?從本質上講,它是一個帶有初始狀態、條件和迭代指令的 While 迴圈。例如,以下通用的 For 迴圈

 for(initialization; condition; increment)
 {
   action
 }

被翻譯成以下虛擬碼 while 迴圈

 initialization;
 while(condition)
 {
   action;
   increment;
 }

這反過來又會被翻譯成以下 Do-While 迴圈

 initialization;
 if(condition)
 {
    do
    {
       action;
       increment;
    } while(condition);
 }

請注意,在 for() 迴圈中,你通常在 A 中分配一個初始常量值(例如 x = 0),然後將該值與 B 中的另一個常量進行比較(例如 x < 10)。大多數最佳化編譯器將能夠注意到 x 第一次確實小於 10,因此不需要初始 if(B) 語句。在這種情況下,編譯器將簡單地生成以下序列

 initialization;
 do
 {
    action
    increment;
 } while(condition);

使程式碼與 while() 迴圈無法區分。

其他迴圈型別

[編輯 | 編輯原始碼]

C 只有 Do-While、While 和 For 迴圈,但其他一些語言很可能實現了它們自己的型別。此外,一個優秀的 C 程式設計師可以很容易地使用一系列好的宏來“自制”一種新的迴圈型別,因此它們值得考慮。

Do-Until 迴圈

[編輯 | 編輯原始碼]

一個常見的 Do-Until 迴圈將採用以下形式

 do
 {
   //loop body
 } until(x);

它本質上變成了以下 Do-While 迴圈

 do
 {
   //loop body
 } while(!x);

Until 迴圈

[編輯 | 編輯原始碼]

與 Do-Until 迴圈一樣,標準的 Until 迴圈看起來像這樣

 until(x)
 {
   //loop body
 }

它(同樣)被翻譯成以下 While 迴圈

 while(!x)
 {
   //loop body
 }

Do-Forever 迴圈

[編輯 | 編輯原始碼]

Do-Forever 迴圈只是一個沒有限定條件的迴圈,它的條件始終為真。例如,以下虛擬碼

 doforever
 {
   //loop body
 }

將變成以下 while 迴圈

 while(1)
 {
   //loop body
 }

這實際上可以簡化為一個簡單的無條件跳轉語句

 beginning:
 ;loop body
 jmp beginning

請注意,一些非最佳化編譯器將為此生成無意義的程式碼

 mov ax, 1
 cmp ax, 1 
 jne loopend
 beginning:
 ;loop body
 cmp ax, 1
 je beginning
 loopend:

請注意,這裡很多比較是不必要的,因為條件是一個常量。大多數編譯器會最佳化這種情況。

華夏公益教科書