2016-06-25 2 views
2

リチャード・ブルムの書籍Professional Assembly Languageでこのテキストが見つかりました。私のCプログラム用に生成されたマシンコードが、本の中で提供されているものと異なるのはなぜですか?

コンパイルするステップは、テキストのプログラミング言語文をアプリケーション 機能を実行するために必要な命令コードに 変換します。各HLLコード行は、 アプリケーションが実行される特定のプロセッサに関連する1つまたは複数の命令コードと一致します。例えば、簡単なHLLコード

int main() 
{ 
    int i = 1; 
    exit(0); 
} 

は、次のIA-32命令コードにコンパイルされています

55 
89 E5 
83 EC 08 
C7 45 FC 01 00 00 00 
83 EC 0C 
6A 00 
E8 D1 FE FF FF 

しかし、私はこのプログラムを自分でしようとすると、私はこれらの結果を再現することはできません。

私のシステムとコンパイラに関する最初のいくつかの詳細。

$ cat /etc/debian_version 
8.3 
$ uname -a 
Linux debian1 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u2 (2016-01-02) x86_64 GNU/Linux 
$ gcc --version 
gcc (Debian 4.9.2-10) 4.9.2 
Copyright (C) 2014 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
$ dpkg -l gcc-multilib 
Desired=Unknown/Install/Remove/Purge/Hold 
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend 
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) 
||/ Name          Version     Architecture    Description 
+++-=========================================-=========================-=========================-======================================================================================== 
ii gcc-multilib        4:4.9.2-2     amd64      GNU C compiler (multilib files) 

ここに私が書いたプログラムです。

$ cat foo.c 
#include <stdlib.h> 

int main() 
{ 
    int i = 1; 
    exit(0); 
} 

以下は、コンパイル後に得られる結果です。

$ gcc -m32 -c foo.c 
$ objdump -d foo.o 

foo.o:  file format elf32-i386 


Disassembly of section .text: 

00000000 <main>: 
    0: 8d 4c 24 04    lea 0x4(%esp),%ecx 
    4: 83 e4 f0    and $0xfffffff0,%esp 
    7: ff 71 fc    pushl -0x4(%ecx) 
    a: 55      push %ebp 
    b: 89 e5     mov %esp,%ebp 
    d: 51      push %ecx 
    e: 83 ec 14    sub $0x14,%esp 
    11: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%ebp) 
    18: 83 ec 0c    sub $0xc,%esp 
    1b: 6a 00     push $0x0 
    1d: e8 fc ff ff ff   call 1e <main+0x1e> 

ここでは、コンパイルとリンク後の結果を示します。

$ gcc -c foo.c 
$ objdump -d a.out | grep -A15 "<main>" 
080483fb <main>: 
80483fb: 8d 4c 24 04    lea 0x4(%esp),%ecx 
80483ff: 83 e4 f0    and $0xfffffff0,%esp 
8048402: ff 71 fc    pushl -0x4(%ecx) 
8048405: 55      push %ebp 
8048406: 89 e5     mov %esp,%ebp 
8048408: 51      push %ecx 
8048409: 83 ec 14    sub $0x14,%esp 
804840c: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%ebp) 
8048413: 83 ec 0c    sub $0xc,%esp 
8048416: 6a 00     push $0x0 
8048418: e8 c3 fe ff ff   call 80482e0 <[email protected]> 
804841d: 66 90     xchg %ax,%ax 
804841f: 90      nop 

08048420 <__libc_csu_init>: 

著者の書籍で提供されている結果を再現するにはどうすればよいですか?

+8

全く同じフラグを持つ完全に同じコンパイラ・バージョンを使用してください。 Cからasmコードを生成する方法はたくさんあるので、一般的にはすべてのコンパイラから同じ出力を期待することはできません。 – Jester

+1

あなたの本は実際にその機械コードの逆アセンブリをリストしていませんか?それはちょっと変だ。 –

答えて

4

本の中で追加の命令ではないが、次のとおりです。

80483fb: 8d 4c 24 04    lea 0x4(%esp),%ecx 
80483ff: 83 e4 f0    and $0xfffffff0,%esp 
8048402: ff 71 fc    pushl -0x4(%ecx) 
8048408: 51      push %ecx 
8048409: 83 ec 14    sub $0x14,%esp 

... 

804841d: 66 90     xchg ax,ax 
804841f: 90      nop 

最初の数行は、16バイト境界にスタックを揃えます。これにより、パフォーマンスが向上し(引数はキャッシュライン境界を越えることができません)、16ビットの境界整列アドレスでのみ動作するSIMD命令の使用が可能になります。

最後のxchg %ax, %axは、2バイトのNOPです。 3バイトのnopは、とにかく到達できないので問題ではありません。それらは、__libc_csu_init機能を適切な位置合わせに埋め込むためにそこにあります。

なぜアセンブリが異なるかについては、アセンブリはプログラミング言語であり、通常は複数の方法があります。 Cプログラムがコンパイラ、同じコンパイラのバージョン、または同じバージョンのコンフィギュレーション間で同じ出力を与えることは期待できません。

具体的なケースでは、16ビットのスタックアライメントは-mpreferred-stack-boundary=4となり、の3バイトのnopとなります。

これらは、gccに電話するとデフォルトの引数として設定されます。直接または-O2またはそれに類するものによって要求されることによって。

+2

'-mpreferred-stack-boundary = 4'(またはそれ以上)は現在[ABIの現在のバージョンで必要です](http://stackoverflow.com/questions/37967710/why-is-esp-masked- with-0xfffffff0/37972101#37972101)。また、gccは 'main'からそのようなものを省略することができます。なぜなら、CRTのスタートアップコードは、Linuxがスタックを16Bで整列させてプロセスを開始した後にスタックを整列させておくからです。しかし、はい、3バイトのNOPはNEXT関数の前に埋め込まれていて、現在の関数の一部ではありません。それはnoreturn関数( 'exit')への' call'で終わりました。 –

+0

なぜ無条件でジャンプするのではなく、noreturn関数を '呼び出す 'ことに迷っているのだろうか。私が紛失していることは明らかですか? –

+1

@CodyGray:なぜテールコールの最適化が行われなかったのか?私は同じことを思った。私は思っていない*私たちはどちらも明らかな何かを欠いている。つまり、関数が復帰すればクラッシュし、 'exit(3)'は関数が失敗しないことを保証します。私はgccが異常なスタックフレームを意味するときにnoreturn関数に 'jmp'を使うという穴の最適化を持っていないと思います。ああ、 'call'は戻りアドレスをプッシュするので、' exit'はその上のargを探します。 gccは 'mov $ 0、4(%esp)'/'jmp exit @ plt'を発行した可能性がありますが、それはより奇妙な穴(コードサイズが悪い) –

関連する問題