序文
この記事では、研究ノートの著者です。運命ならば、批判のサプリメントを歓迎します。
以下の場合:
- 各ステップの非常に明確な内容をプログラムのコンパイルプロセスを暗示が、そうではありません
- デバッガに精通していません
- 何が起こったかの背後にあるプログラムとプログラミング言語の詳細については、
- 何もの人々が技術的な記事を見て、無料ではありません
この記事は含まれていません。
- プログラムの最適化プログラム
- ファッションデザインプログラム
- 便利なソフトウェアのスキルを書きます
基本:
- C言語のスキルで書かれた「ノーマル」をマスターしています
- 私は、コンピュータ用語とその意味では、いくつかの「共通」を知っています
- 文書を照会することにより、(人間 の未知を使用してAPIの概要を説明し、グーグル)
コード
本明細書で使用する場合、次のコード(のhello.c)。
1の#include <stdio.hの> 2 3 int型のmain(int型 ARGC、チャー *のARGV []){ 4 5 ASM 揮発性(" #のprintf開始" )。 6 7 のprintf(" こんにちは、世界%D%sの\ nは!"、のargc、argvの[ 0 ]); 8 9 ASM 揮発性(" #のprintf端" )。 10 11 戻り 0 ; 12 }
読み取りアセンブリコードの生成を容易にするために、我々は、これらの文字列のルックスから生成されたアセンブリコードを配置することができるので、ちょうど前後で、関数呼び出しに加えて、特定のIDの後に見てみたいです。
次のように加えて、GCCを使用してコンパイルされ、コマンドは次のとおりです。
$ gccの hello.cの-Wall -g -O0 -S - O hello.s $ gccの hello.s - O hello.outの $ objdumpの -d hello.outの> hello.asm
ここでhello.asmの作者で一部の最後の生成環境の主な機能です。
もちろん、あなたが.Sファイルを読み取ることができますが、CPUの命令実行の一部のみが、その後、両方の内容は同じです。
000000000000064a <メイン>: 64 : 55 プッシュ %のRBP 64 B: 48 89 E5 のMOV %のRSPは、%RBP 64 E: 48 83、EC 10 、サブ $ 0x10の、%RSP 652: 89 7D FC MOV %EDI、-0x4(%のRBP) 655: 48 89 75 F0の MOVの %のRSI、-0x10(%のRBP) 659: 48 8B 45 F0 MOV -0x10(%のRBP)、%のRAX 65 D: 48 8B 10 MOV (%のRAX)、%RDX 660:8B 45 FCの MOVの -0x4(%のRBP)、%EAX 663: 89 C6の MOVの %のEAX、%ESI 665: 48 8D 3D 98 00 00 00 LEA 0×98 (%のRIP)、%RDI#704 <_IO_stdin_used + 0x4の> 66個のC: B8 00 00 00 00 のmov $ 0x0の、%eaxに 671:E8 AA FE FF FF callq 520 <printfのPLT @> 676:B8 00 00 00 00 のmov $ 0x0の、%eaxに 67 B: C9 leaveq 67 C: C3 retq 67 D: 0F 1F 00 nopl(%のRAX)
解決
公式ドキュメント
:インテルは、インテル64およびIA-32アーキテクチャーマニュアルは、このサイトで見つけることができ開示さ
https://software.intel.com/en-us/articles/intel-sdm
公式サイトでは、パフォーマンスを最適化するために、文書、文書などの拡張命令セットを発表しました。
登録
x86アーキテクチャでは、Eの先頭に64ビットレジスタrで始まる、32ビットのレジスタです。
GPR(汎用レジスタ)登場属し上記のアセンブリ・コード・レジスタ、それはプログラムが自由にこれらのレジスタを操作し、そのメモリの使用と違いがないことができると言うことです。
上記のコンパイルコマンドで、一緒に(例えば、括弧内のレジスタマーク付き )RAXの%( )、値を保持するメモリアドレスにこのレジスタ・ポイントの代表値。
単語を説明するためのC言語です (* INT *)RAX 。
括弧の前の値があれば、その後、値を保持するメモリアドレスの減算値レジスタ後の値を表します。
例えば -0x10(%のRBP) メモリことを意味 RBP 16(注16進数)だけ減分値が、位置を記憶します。
単語を説明するためにC言語と同じである *(INT *)((CHAR *)RBP - が0x10) 。
C言語の解釈とトップは正確ではないかもしれません。
具体的には、 MOV とデータサイズが指定されていない、つまり、ソース・データ・レジスタのサイズであり、最初のパラメータに依存するデータの特定の32ビットまたは64ビットのコピーで言うことです。
著者は、652枚の分析と考えて MOVの コピーは、32ビットのデータ、及び655は、 MOVの コピー64ビットのデータ。
注意すべき事は、そのようなことであります RAX と EAXが 実際には2つの別々のレジスタではありません。 EAX 参照 RAX 32以下です。
だけでなく、 AX 、 AH 、 アル 。
スタック
コンパイラ RSP (32ビットレジスタである ESP スタックポインタ(SP、スタックポインタ)として専用)。
SPは、より小さい、すなわち低下に向かう方向に延びるのx86である RSPの スタック空間のこの部分を得るに等しい値を。
さらに、 RSPは (すなわち、16バイトに整列されなければならない RSPの 値が16の倍数でなければなりません)。
しかし、メインで同様のコマンドがありませんでした。
私は次のようにコマンドを整列スタートは、部分的に、コンパイルがあるが見つかりました:
0000000000000540 <_start>: 540: 31編の XORの 場合%ebp、%のEBP 542: 49 89 D1 のmov %のRDX、%R9 545:5Eの ポップ %のRSI 546: 48 89 E2 のmov %のRSP、%のRDX 549: 48の 83 E4のF0 と $ 0xfffffffffffffff0、%のRSP 54 D: 50 プッシュ %のRAX 54 E: 54 プッシュ %のRSP 54 F: 4C部8d 05 9A 01 00 00 LEAの 0x19a(%のRIP)、%R8#1 6f0 <__ libc_csu_fini> 556: 48 8D 0D 23 01 00 00 LEA 0x123(%のRIP)、%RCX#680 <__ libc_csu_init> 55 D: 48 8D 3D E6 00 00 00 LEAの 0xe6(%のRIP)、%RDI位部64a <メイン> 564:FF 15 76 0A 20 00 callq * 0x200a76(%RIP)#2 00fe0 <__ libc_start_mainする@ GLIBC_2。2。5 > 56 : F4 HLT 56 B: 0F 1F 44 00 00 noplの0x0の(%のRAX、%のRAX、1)
コマンド564は、この命令がメインの前に実行する必要がありますね観察することができます。
そしてコマンド549は、整列される のRSP (のコマンド RSP すなわち、16の倍数4が0になる最後に、)。
関数呼出し
32ビットアーキテクチャでは、パラメータが(レジスタの拡張子を持っていると思われる)空間をスタックする関数呼び出しである場合。
64ビットアーキテクチャでは、レジスタの数が増加する(以降 R8 〜 R15 )、多くの場合、パラメータレジスタに直接であってもよいです。
元のアセンブリコードブックは、32ビットアーキテクチャを使用して、著者は、アセンブラコードを解析し64であるので、段差関数呼び出しの小さな差はありません。
それらのほとんどは、以下の私の個人的な分析です。
「レジスタ内のパラメータは、」この条件は、主な機能にも同様に適用されるべきです。
メイン関数のパラメータは、(二つがある)に別々に格納する必要があり 、EDI 及び RSI それらを。最初のものはある のint 型の、ここでは32、それらが置かれた EDIを 内部。64ビットの64ビットアーキテクチャでポインタの配列(ポインタである)間違い、それに格納されている RSI これら。
次のステップでは、原因が呼び出される のprintf 関数を、私たちは最初に必要な EDI と RSI スタックに格納された値。これらのレジスタの値を変更するように。したがって、16のプログラム最初のSP値は、それが16バイトスタック空間を拡大、縮小されます。
次に、プログラムがスタックに格納されている2つのパラメータを置きます。
そしてその後、コマンドの次のシリーズ、最初の店舗 のargv [ 0 ] に RDX 再び、 ARGC に格納された ESI それら、および最後に再びフォーマットされた文字列(アドレス)に格納されている RDI それら。
内部で書かれたプログラムの実行とフォーマット文字列は、アドレスからの時間取るために使用することができます リップ これは推測される(PCは64ビットのレジスタを保存するために使用される)レジスタを。実装でこの文字列を変更することができない理由です。
次に、コマンドの関数呼び出しがあります。そして返します。
NOPは 何も(無操作)を行わないことを意図しています。ここで埋める NOP (67D後方番号3は、16バイトにアラインされ680、であることを起こる)が背面位置合わせの機能16のバイトを可能にすることです。
私は破壊と信じ EDI と RSIは 、これらの値ために使用されていないスタックのバックから回復しませんでした。この推測は、より深い検査をしませんでした。
概要
このセクションでは、私は基本的な汎用レジスタと呼ばれる機能モードを学びました。
洗礼の日の後、それは今ええ、サブCPUを理解するのに約10億5におけるプログラムの割合ですることができます。