11

GCC コンパイラを使用してコードをアセンブルし、Mac のアセンブリを学習し始めたところです。残念ながら、初心者がこれを行う方法を学ぶためのリソースは非常に限られています。私はついに頭を悩ませ始めることができるいくつかの簡単なサンプルコードを見つけることができ、それを正しく組み立てて実行することができました. コードは次のとおりです。

.text                                           # start of code indicator.
.globl _main                                    # make the main function visible to the outside.
_main:                                          # actually label this spot as the start of our main function.
    push    %rbp                            # save the base pointer to the stack.
    mov     %rsp, %rbp                      # put the previous stack pointer into the base pointer.
    subl    $8, %esp                        # Balance the stack onto a 16-byte boundary.
    movl    $0, %eax                        # Stuff 0 into EAX, which is where result values go.
    leave                                   # leave cleans up base and stack pointers again.
    ret

コメントはコード内のいくつかのことを説明しています (2 行目から 5 行目で何をしているのかはある程度理解できます) が、これが何を意味するのか理解できません。レジスタとは何か、ここにある各レジスタ ( rbprspespおよびeax) が何に使用され、どのくらいの大きさであるかの基本は理解しています。また、(一般的に) スタックが何であるかも理解していますが、これはまだ頭を悩ませています。誰がこれが何をしているのか正確に教えてもらえますか? また、初心者向けの優れたチュートリアルの方向性を教えてもらえますか?

4

1 に答える 1

20

スタックは、LIFO 原則に従うデータ構造です。日常生活 (コンピューターの外) のスタックは上向きに成長しますが、x86 および x86-64 プロセッサのスタックは下向きに成長します。x86 スタックに関する Wikibooks の記事を参照してください (ただし、コード例は Intel 構文の 32 ビット x86 コードであり、コードは AT&T 構文の 64 ビット x86-64 コードであることを考慮してください)。

それで、あなたのコードは何をしますか(ここでの私の説明はインテルの構文です):

push %rbp

スタックにプッシュrbpし、実質的に から 8 を引いてrsp( のサイズrbpが 8 バイトであるため)、 にストアrbp[ss:rsp]ます。

したがって、インテルの構文では、push rbp実際にこれを行います。

sub rsp, 8
mov [ss:rsp], rbp

それで:

mov     %rsp, %rbp

これは明らかです。rspの値をに格納するだけrbpです。

subl    $8, %esp

から 8 を引きesp、 に格納しespます。ここでは問題が発生しない場合でも、実際にはこれはコードのバグです。x86-64 で宛先として32 ビット レジスタ ( eaxebxecxedxebp、または)を持つ命令は、対応する 64 ビット レジスタ ( 、、、esp、または)の最上位 32 ビットをゼロに設定し、スタックを引き起こします。 4 GiB 制限より下のどこかを指すポインターであり、効果的にこれを行います (Intel 構文で):esiediraxrbxrcxrdxrbprsprsirdi

sub rsp,8
and rsp,0x00000000ffffffff

編集:以下の結果を追加しましsub esp,8た。

ただし、メモリが 4 GiB 未満のコンピューターでは問題ありません。4 GiB を超えるメモリを搭載したコンピューターでは、セグメンテーション エラーが発生する可能性があります。leaveコードのさらに下では、適切な値が に返されますrsp。通常、x86-64 コードでは、決して必要ありespません (おそらくいくつかの最適化や微調整を除く)。このバグを修正するには:

subq    $8, %rsp

ここまでの命令は標準的なエントリ シーケンスです ($8スタックの使用状況に応じて置き換えます)。Wikibooks には、x86 関数とスタック フレームに関する有用な記事があります(ただし、AT&T 構文の 64 ビット x86-64 アセンブリではなく、Intel 構文の 32 ビット x86 アセンブリを使用していることに注意してください)。

それで:

movl    $0, %eax

これは明らかです。に 0 を格納しeaxます。これはスタックとは関係ありません。

leave

これは、mov rsp, rbpその後にpop rbp.

ret

そして、これは最後に、 にrip格納されている値に設定され[ss:rsp]、コード ポインタをこのプロシージャが呼び出された場所に戻して、 に 8 を加算しrspます。

于 2013-01-12T21:34:35.090 に答える