簡體   English   中英

表示為匯編代碼的C代碼-如何解釋?

[英]C Code represented as Assembler Code - How to interpret?

我得到了這段簡短的C代碼。

#include <stdint.h>
uint64_t multiply(uint32_t x, uint32_t y) {

uint64_t res;
res = x*y;
return res;
}

int main() {

uint32_t a = 3, b = 5, z;
z = multiply(a,b);
return 0;
}

上面的給定C代碼也有一個匯編程序代碼。 我不了解所有的匯編代碼。 我評論了每一行,您會在每一行的評論中找到我的問題。

匯編代碼是:

.text
multiply:
     pushl  %ebp  // stores the stack frame of the calling function on the stack
     movl   %esp, %ebp // takes the current stack pointer and uses it as the frame for the called function
     subl   $16, %esp // it leaves room on the stack, but why 16Bytes. sizeof(res) = 8Bytes
     movl   8(%ebp), %eax // I don't know quite what "8(%ebp) mean? It has to do something with res, because
     imull  12(%ebp), %eax // here is the multiplication done. And again "12(%ebp).
     movl   %eax, -8(%ebp) // Now, we got a negative number in front of. How to interpret this?
     movl   $0, -4(%ebp) // here as well
     movl   -8(%ebp), %eax // and here again.
     movl   -4(%ebp), %edx // also here
     leave
     ret
main:
     pushl  %ebp // stores the stack frame of the calling function on the stack
     movl   %esp, %ebp // // takes the current stack pointer and uses it as the frame for the called function
     andl   $-8, %esp // what happens here and why?
     subl   $24, %esp // here, it leaves room for local variables, but why 24 bytes? a, b, c: the size of each of them is 4 Bytes. So 3*4 = 12
     movl   $3, 20(%esp) // 3 gets pushed on the stack
     movl   $5, 16(%esp) // 5 also get pushed on the stack
     movl   16(%esp), %eax // what does 16(%esp) mean and what happened with z?
     movl   %eax, 4(%esp) // we got the here as well
     movl   20(%esp), %eax // and also here
     movl   %eax, (%esp) // what does happen in this line?
     call   multiply  // thats clear, the function multiply gets called
     movl   %eax, 12(%esp) // it looks like the same as two lines before, except it contains the number 12
     movl   $0, %eax // I suppose, this line is because of "return 0;"
     leave
     ret

相對於%ebp的負引用是針對堆棧上的局部變量的。

 movl   8(%ebp), %eax // I don't know quite what "8(%ebp) mean? It has to do something with res, because`

%eax = x

 imull  12(%ebp), %eax // here is the multiplication done. And again "12(%ebp).

%eax =%eax * y

 movl   %eax, -8(%ebp) // Now, we got a negative number in front of. How to interpret this?

(u_int32_t)res =%eax //設置低32位res

 movl   $0, -4(%ebp) // here as well

清除res的高32位,以將32位乘法結果擴展到uint64_t

 movl   -8(%ebp), %eax // and here again.
 movl   -4(%ebp), %edx // also here

返回ret // 64位結果作為一對32位寄存器%edx:%eax返回

至於主要方面,請參見x86調用約定 ,這可能有助於弄清發生的情況。

 andl   $-8, %esp // what happens here and why?

堆棧邊界以8對齊。我相信這是ABI要求

 subl   $24, %esp // here, it leaves room for local variables, but why 24 bytes? a, b, c: the size of each of them is 4 Bytes. So 3*4 = 12

8的倍數(可能是由於對齊要求)

 movl   $3, 20(%esp) // 3 gets pushed on the stack

a = 3

 movl   $5, 16(%esp) // 5 also get pushed on the stack

b = 5

 movl   16(%esp), %eax // what does 16(%esp) mean and what happened with z?

%eax = b

z為12(%esp),尚未使用。

 movl   %eax, 4(%esp) // we got the here as well

將b放在堆棧上(multiple()的第二個參數)

 movl   20(%esp), %eax // and also here

%eax = a

 movl   %eax, (%esp) // what does happen in this line?

將一個放在堆棧上(multiple()的第一個參數)

 call   multiply  // thats clear, the function multiply gets called

乘法返回%edx:%eax中的64位結果

 movl   %eax, 12(%esp) // it looks like the same as two lines before, except it contains the number 12

z =(uint32_t)乘法()

 movl   $0, %eax // I suppose, this line is because of "return 0;"

對。 返回0;

調用函數時,參數會被壓入堆棧。 在函數內部,此時的堆棧指針將另存為基本指針。 (您已經獲得了很多。)基本指針用作固定的位置,從該位置可以引用參數(在它上面,因此是正偏移量)和局部變量(在它下面,因此是負偏移量)。

使用基本指針的優點是,即使堆棧指針發生更改(由於函數調用和新作用域),它也可以在整個函數中保持穩定。

因此8(%ebp)是一個參數,而12(%ebp)是另一個參數。

該代碼可能在堆棧上使用了比其需要的空間更多的空間,因為它使用的臨時變量可以在優化被打開的情況下進行優化。

您可能會發現這很有幫助: http : //en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames

我開始輸入此內容作為評論,但已太長而無法容納。

您可以使用-masm=intel編譯示例,從而使程序集更具可讀性。 另外,請勿將pushpop指令與mov混淆。 在取消引用地址之前, pushpop 總是分別增加和減少esp ,而mov則沒有。

有兩種方法可以將值存儲到堆棧中。 您可以一次push每個項目push一個項目,也可以預先分配所需的空間,然后使用mov +相對espebp相對偏移將每個值加載到堆棧槽中。

在您的示例中,gcc選擇了第二種方法,因為它通常更快,因為與第一種方法不同,在將值保存到堆棧之前,您並不會不斷地增加esp

為了解決您在注釋中遇到的其他問題,x86指令集沒有可將值直接從存儲位置a復制到另一個存儲位置bmov指令。 看到類似以下代碼的情況並不少見:

  mov   eax, [esp+16]
  mov   [esp+4], eax
  mov   eax, [esp+20]
  mov   [esp], eax
  call  multiply(unsigned int, unsigned int)
  mov   [esp+12], eax

寄存器eax用作中間臨時變量,以幫助在兩個堆棧位置之間復制數據。 您可以將以上內容翻譯為:

esp[4] = esp[16]; // argument 2
esp[0] = esp[20]; // argument 1
call multiply
esp[12] = eax;    // eax has return value

這是調用multiply之前堆棧的大致樣子:

lower addr    esp       =>  uint32_t:a_copy = 3 <--.  arg1 to 'multiply'
              esp + 4       uint32_t:b_copy = 5 <--.  arg2 to 'multiply'
    ^         esp + 8       ????
    ^         esp + 12      uint32_t:z = ?      <--.
    |         esp + 16      uint32_t:b = 5         |  local variables in 'main'
    |         esp + 20      uint32_t:a = 3      <--.
    |         ...
    |         ...
higher addr   ebp           previous frame

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM