2016-04-02 3 views
0

私はgdbを実行し、メインとget_inputで逆アセンブルを使用するように、Cプログラムがアセンブリレベルでどのように見えるかを理解しようとしています。プログラムは短く、私はそれに追随することができます。 私は理解していない2行があります。まず、上のメインでは()である:アセンブリ内のトレースプログラム。

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

私たちは、RBPの古い値を保存し、RBPへのRSPの現在の値を保存します。その指示の目的は何ですか?ここで

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

をあまりにも我々は、スタックにそれを押して、RBPの古い値を保存することで起動します。

(get_inputの他)があります。次に、rspに現在の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; 
} 

主な機能のためのアセンブラコードのダンプ:

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のためのアセンブラコードのダンプ:main()については

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 <[email protected]> 
    0x0000000000400591 <+20>: lea -0x10(%rbp),%rax 
    0x0000000000400595 <+24>: mov %rax,%rdi 
    0x0000000000400598 <+27>: callq 0x400450 <[email protected]> 
    0x000000000040059d <+32>: leaveq 
    0x000000000040059e <+33>: retq 
+3

'mov $ 0x0、%eax'は呼び出し規約に従って' eax'に返される戻り値0です。 16バイトについてはスタックアライメントであり、呼び出し規約によっても規定されています。 – Jester

+0

@Jester、あなたはあまりにも速く私のために急いでいる。私が何かを投稿しようとするたびに、私はそれを書き終えるまでに何かをここに持ってきました! –

答えて

3

...

0x000000000040059f <+0>: push %rbp 

プッシュスタック上の3210の値。 %RBP

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

コピー%RSPの値(新しいスタックフレームを作成します)。

0x00000000004005a3 <+4>: mov $0x0,%eax 
%EAXに即値 0x0を移動し

。つまり、%EAXがゼロになります。 64ビットモードでは、%RAXもすべてクリアされます。

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

次にプッシュ%RIPの値(取り消し直接)、ラベル/機能get_input()にジャンプ。

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

AMD64 System V ABIによれば、関数の戻り値は、(アカウントに浮動小数点および大きな構造を取っていない)%RAXに格納されています。また、レジスタのグループには、呼び出し元保存と呼び出し先保存の2つがあります。関数を呼び出すときに、呼び出し元に保存されたレジスタを同じままにすることはできません。必要に応じて、スタックに保存する必要があります。同様に、呼び出される関数は、呼び出し先で保存されたレジスタを使用する場合、それらを保持する必要があります。呼び出し側で退避したレジスタは%RAX%RDI%RSI%RDX%RCX%R8%R9%R10、および%R11です。呼び出し先保存レジスタは、%RBX,%RSP,%RBP,%R12,%R13,%R14および%R15である。main()が明らかにreturn 0を実行して

は今、それは右、その0%RAXに戻らなければなりませんか?しかし、2つのことを考慮する必要があります。まず、AMD64 System V ABIでは、sizeof(int) == 4です。 %RAXは8バイト幅ですが、%EAXは4バイト幅ですので、main()の戻り値のように、int全体の処理には%EAXを使用する必要があります。第2に、%EAX%RAXの一部であり、%RAXは呼び出し元が保存されているため、呼び出し後にその値に依存することはできません。したがって、関数の戻り値を0に設定するには、MOV $0x0, %EAXを実行します。

0x00000000004005b2 <+19>: pop %rbp 

main()のスタックフレームの呼び出し側の%RBPは、つまり、main()を破壊する "復元します。

0x00000000004005b3 <+20>: retq 

0の戻り値を持つmain()から戻ります。

はその後、我々はスタックにget_input() ...

0x000000000040057d <+0>: push %rbp 

プッシュ%RBPの価値を持っています。 %RBP

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

コピー%RSPの値(新しいスタックフレームを作成します)。

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

%RSP(現在のフレームの一時記憶の予備16バイト)から減算16。

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

%RAXに実効アドレス-0x10(%RBP)をロードします。つまり、%RBPの値から16を引いた結果を%RAXにロードします。これは、%RAXがローカル一時記憶域の最初のバイトを指すようになったことを意味します。

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

は、ABIによれば、関数の最初の引数は、この場合には、%RAXの値をTO-BE-の最初の引数として与えられる...等%RDI%RSIに第二、上に与えられていますいわゆる機能。

0x000000000040058c <+15>: callq 0x400480 <[email protected]> 

コール機能gets()

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

上記と同じ。

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

第1引数として%RAXを渡します。

0x0000000000400598 <+27>: callq 0x400450 <[email protected]> 

コール機能puts()

次いで MOV %RBP, %RSPに相当
0x000000000040059d <+32>: leaveq 

POP %RBP、すなわち、スタックフレームを破棄。

0x000000000040059e <+33>: retq 

戻り値get_input()適切な戻り値なし。

今...

MOV $0x0, %EAX その命令の目的は何ですか?

この命令の2番目のインスタンスは、戻り値がmain()に設定されているため、非常に重要です。しかし、最初のものは実際には冗長です。コンパイラで最適化が無効になっている可能性があります。

次に、rspから16バイトを減算します。私はスペースが割り当てられていることを理解していますが、なぜそれは16バイトで8バイトではないのですか?私はバッファを8バイトだけ作成しました、他の8バイトの目的は何ですか?

ABIでは、各ファンクションコールの前に、%RSPを16バイト境界に配置する必要があります。ところで、静的サイズのバッファと、gets()から離れるべきです。

+0

これまでに時間をかけていただきありがとうございます。今はすべて意味があります。 –

1

最初の命令mov $0x0, %eaxは、戻りコードを設定するためにEAXにゼロを移動します。

第2の命令sub $0x10,%rspは、システムコールのためにメモリを割り当て、スタックをアライメントしています。呼び出し標準では、8バイトではなく16バイトの位置合わせが必要です。

関連する問題