簡體   English   中英

匯編中的跟蹤程序。

[英]Tracing program in assembly.

我試圖了解C程序在匯編級別的外觀,因此我運行gdb並在main和get_input上使用了反匯編。 該程序很短,所以我可以更好地遵循它。 我不明白有2行。 首先在main()中是:

0x00000000004005a3 <+4>: mov $0x0,%eax

我們保存舊的rbp值,並將rsp的當前值保存到rbp。 該指令的目的是什么?

get_input()中的另一個是:

000000000400581 <+4>:   sub    $0x10,%rsp

在這里,我們也從保存舊值rbp開始,將其壓入堆棧。 然后為rbp給出rsp的當前值。 然后從rsp中減去16個字節。 我知道這是分配的空間,但是為什么它是16個字節而不是8個字節? 我只將緩沖區設置為8個字節,其他8個字節的用途是什么?

#include <stdio.h>
void get_input()
{
    char buffer[8];

    gets(buffer);
    puts(buffer);
}

int main()
{
    get_input();
    return 0;
}

函數main的匯編代碼轉儲:

   0x000000000040059f <+0>: push   %rbp
   0x00000000004005a0 <+1>: mov    %rsp,%rbp
   0x00000000004005a3 <+4>: mov    $0x0,%eax
   0x00000000004005a8 <+9>: callq  0x40057d <get_input>
   0x00000000004005ad <+14>:    mov    $0x0,%eax
   0x00000000004005b2 <+19>:    pop    %rbp
   0x00000000004005b3 <+20>:    retq   
End of assembler dump.

函數get_input的匯編代碼轉儲:

   0x000000000040057d <+0>: push   %rbp
   0x000000000040057e <+1>: mov    %rsp,%rbp
   0x0000000000400581 <+4>: sub    $0x10,%rsp
   0x0000000000400585 <+8>: lea    -0x10(%rbp),%rax
   0x0000000000400589 <+12>:    mov    %rax,%rdi
   0x000000000040058c <+15>:    callq  0x400480 <gets@plt>
   0x0000000000400591 <+20>:    lea    -0x10(%rbp),%rax
   0x0000000000400595 <+24>:    mov    %rax,%rdi
   0x0000000000400598 <+27>:    callq  0x400450 <puts@plt>
   0x000000000040059d <+32>:    leaveq 
   0x000000000040059e <+33>:    retq 

對於main() ...

0x000000000040059f <+0>: push   %rbp

%RBP的值壓入堆棧。

0x00000000004005a0 <+1>: mov    %rsp,%rbp

%RSP的值復制到%RBP (創建一個新的堆棧框架)。

0x00000000004005a3 <+4>: mov    $0x0,%eax

將立即值0x0移動到%EAX 也就是說,它將%EAX 當您處於64位模式時,這還將清除所有%RAX

0x00000000004005a8 <+9>: callq  0x40057d <get_input>

推送%RIP的值(直接get_input()撤消),然后跳轉到label / function get_input()

0x00000000004005ad <+14>:    mov    $0x0,%eax

根據AMD64 System V ABI ,函數的返回值存儲在%RAX (不考慮浮點和大型結構)。 它還說有兩組寄存器:保存呼叫者和保存被呼叫者。 調用函數時,不能期望保存調用者的寄存器保持不變,必須在必要時將它們自己保存在堆棧中。 同樣,被調用的函數必須使用被調用方保存的寄存器。 調用方保存的寄存器為%RAX%RDI%RSI%RDX%RCX%R8%R9%R10%R11 被調用者保存的寄存器為%RBX%RSP%RBP%R12%R13%R14%R15

現在,由於main()顯然執行return 0 ,所以它必須在%RAX返回0 ,對吧? 但是,應考慮兩點。 首先,在AMD64系統V ABI中, sizeof(int) == 4 %RAX寬度為8個字節,但%EAX寬度為4個字節,因此應使用%EAX來處理int范圍的東西,例如main()的返回值。 其次, %EAX%RAX一部分,而%RAX是保存呼叫者的,因此在調用后我們不能依靠它的值。 因此,我們執行MOV $0x0, %EAX ,以將函數的返回值設置為零。

0x00000000004005b2 <+19>:    pop    %rbp

恢復main()的調用者的%RBP ,即銷毀main()的堆棧幀。

0x00000000004005b3 <+20>:    retq   

main()返回,返回值為0

然后,我們有get_input() ...

0x000000000040057d <+0>: push   %rbp

%RBP的值壓入堆棧。

0x000000000040057e <+1>: mov    %rsp,%rbp

%RSP的值復制到%RBP (創建一個新的堆棧框架)。

0x0000000000400581 <+4>: sub    $0x10,%rsp

%RSP減去16(為當前幀保留16個字節的臨時存儲空間)。

0x0000000000400585 <+8>: lea    -0x10(%rbp),%rax

將有效地址-0x10(%RBP)加載到%RAX 也就是說,它將%RBP的值減去16的結果加載到%RAX 這意味着%RAX現在指向本地臨時存儲的第一個字節。

0x0000000000400589 <+12>:    mov    %rax,%rdi

根據ABI的說法,函數的第一個參數在%RDI上給出,第二個參數在%RSI等等,在這種情況下, %RAX的值作為要調用的函數的第一個參數給出。

0x000000000040058c <+15>:    callq  0x400480 <gets@plt>

調用函數gets()

0x0000000000400591 <+20>:    lea    -0x10(%rbp),%rax

與上述相同。

0x0000000000400595 <+24>:    mov    %rax,%rdi

%RAX作為第一個參數傳遞。

0x0000000000400598 <+27>:    callq  0x400450 <puts@plt>

調用函數puts()

0x000000000040059d <+32>:    leaveq

MOV %RBP, %RSP等效MOV %RBP, %RSP然后POP %RBP ,即破壞堆棧幀。

0x000000000040059e <+33>:    retq

從函數get_input()返回而沒有正確的返回值。

現在...

MOV $0x0, %EAX該指令的目的是什么?

該指令的第二個實例非常重要,因為它設置了main()的返回值。 但是,第一個實際上是多余的。 您可能在編譯器上禁用了優化。

然后從rsp中減去16個字節。 我知道這是分配的空間,但是為什么它是16個字節而不是8個字節? 我只將緩沖區設置為8個字節,其他8個字節的用途是什么?

ABI要求將%RSP定位在每個函數調用之前的16字節邊界上。 順便說一句,您應該遠離靜態大小的緩沖區和gets()

第一條指令mov $0x0, %eax將零移動到EAX中以設置返回碼。

第二條指令sub $0x10,%rsp分配內存並為系統調用對齊堆棧。 調用標准要求16字節對齊,而不是8。

暫無
暫無

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

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