質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • C

    4543questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • アセンブリ言語

    116questions

    アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

アセンブリとC言語

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,327

strike1217

score 568

以下のようなプログラムを作ってみました。

include <stdio.h>

int main(){
int i = 9;
printf("%d", i);
return 0;
}

これを逆アセンブルしたところ以下のようになりました。

0000000100000f50 <_main>:
100000f50:    55                       push   %rbp
100000f51:    48 89 e5                 mov    %rsp,%rbp
100000f54:    48 83 ec 10              sub    $0x10,%rsp
100000f58:    48 8d 3d 47 00 00 00     lea    0x47(%rip),%rdi        # 100000fa6 <_main+0x56>
100000f5f:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%rbp)
100000f66:    c7 45 f8 09 00 00 00     movl   $0x9,-0x8(%rbp)
100000f6d:    8b 75 f8                 mov    -0x8(%rbp),%esi
100000f70:    b0 00                    mov    $0x0,%al
100000f72:    e8 0d 00 00 00           callq  100000f84 <_main+0x34>
100000f77:    31 f6                    xor    %esi,%esi
100000f79:    89 45 f4                 mov    %eax,-0xc(%rbp)
100000f7c:    89 f0                    mov    %esi,%eax
100000f7e:    48 83 c4 10              add    $0x10,%rsp
100000f82:    5d                       pop    %rbp
100000f83:    c3                       retq   

int i = 9; の部分が movl   $0x9,-0x8(%rbp) に相当するのはわかるのですが、
この -0x8 は何ですか??

それと、前後の
lea    0x47(%rip),%rdi        # 100000fa6 <_main+0x56>
movl   $0x0,-0x4(%rbp)

mov    -0x8(%rbp),%esi
mov    $0x0,%al

この4つは何をしているのでしょうか??
教えてください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

昔コンパイラを作っていた経験から、説明します。
このCコンパイラは、コードの最適化を図っているようで、若干、出力されたアセンブラとC言語プログラム行が一致せず、一部が前後しているようです。そこで、元のC言語の順にどのように出力されているか説明します。

前の方が書かれているように
0000000100000f50 <_main>: 
100000f50:    55                       push   %rbp 
100000f51:    48 89 e5                 mov    %rsp,%rbp 
100000f54:    48 83 ec 10              sub    $0x10,%rsp 
は、関数に入った時に自動的に出力されるプロローグ処理と呼ばれている部分です。%rbp(ベースポインター)で示されるアドレスから0x10離れた位置に%rsp(スタックポインタ)に設定しています。この間には、関数内で利用する変数などの領域が格納されます。このような変数の取り方を行うことで、再帰呼び出しをしてもそれぞれの関数内で値を保持することができます。通常、スタックポインタは、上下することがあるので、変数を参照する場合には、このベースポインタからの相対位置でアクセスします。

したがって、「int i = 9」は、
100000f66:    c7 45 f8 09 00 00 00     movl   $0x9,-0x8(%rbp) 
となります。

次の「printf("%d",i);」は、
100000f58:    48 8d 3d 47 00 00 00     lea    0x47(%rip),%rdi # 100000fa6 <_main+0x56> 
100000f6d:    8b 75 f8                 mov    -0x8(%rbp),%esi 
100000f70:    b0 00                    mov    $0x0,%al 
100000f72:    e8 0d 00 00 00           callq  100000f84 <_main+0x34>
100000f79:    89 45 f4                 mov    %eax,-0xc(%rbp) 
となります。
先頭のleaは、”%d”の文字列アドレスを%rdiレジスタ格納するものです。%ripは、命令を実行するメモリ位置を保持するレジスタであり、 0x47(%rip)とは、実行している命令から0x47先であり、具体的には100000fa6 <_main+0x56> となります。"%d"などのようにプログラム中に現れる文字列は変更さることがないので、命令コードが書かれている領域に格納するケースがあります。
次の
100000f6d:    8b 75 f8                 mov    -0x8(%rbp),%esi
で、i変数「-0x8(%rbp)」の値を%esiに格納しています。
100000f70:    b0 00                    mov    $0x0,%al 
については、ちょっとわかりませんが、可能性があるとするならば、callによって別のサブルーチンを呼び出した後に取得する返却値をあらかじめクリアすることで、サブルーチン側で alレジスタに値を設定していない場合に不正が値が返却されるのを防いでいるのかもしれません。
最後の
100000f79:    89 45 f4                 mov    %eax,-0xc(%rbp) 
で、printfからの返却値を「-0xc(%rbp) 」に格納しているようです。

最後に、返却値に0を設定して返却する部分(エピローグ処理)となります。
100000f77:    31 f6                    xor    %esi,%esi 
100000f7c:    89 f0                    mov    %esi,%eax 
100000f7e:    48 83 c4 10              add    $0x10,%rsp 
100000f82:    5d                       pop    %rbp 
100000f83:    c3                       retq   
となります。
xor %esi,%esiで%esiレジスタをクリアし、mov %esi,%eaxで、その値を%eaxレジスタに格納しています。直接、%eaxに0をダイレクトに設定して良いのですが、メモリを少なくしたり、高速に動作するために、xor命令を使うケースは多くあります。
最後にスタックポインターをベースポインター位置に戻り、retq命令を実行することで、呼び出し側命令位置に戻ります。
私自身、インテル系の8bitCPUや16bitCPUのアセンブラしか知りませんが、おそらくこのような動作になっているかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/10 13:17

    ありがとうございます。
    分かりやすいです。

    キャンセル

  • 2016/09/10 13:26

    lea 0x47(%rip),%rdi
    mov -0x8(%rbp),%esi
    この2つはprintf()の第1パラメータと第2パラメータ を設定していますが、rdiとesiのレジスタをどうやって、printfに渡しているのでしょうか?

    キャンセル

  • 2016/09/10 15:11 編集

    関数は、引数の数が2つとは限りません。引数の数が多くなるとレジスタで渡すことができなくなる可能性があります。このため、通常は、スタックに積んで渡すのが通常です。特に、printf関数は、引数の数が不定なのでなおさらです。
    この例では、レジスタ渡しする場合には、その方式(第一パラメータは、%rdiレジスタに格納するなどのルール)に合った関数を呼んでいるのではないでしょうか?

    キャンセル

checkベストアンサー

0

手元に、同じ結果が得られるコンパイラがないので想像な部分がありますが、
まず、-0x08についてです。
関数の入り口で、

mov     %rsp,%rbp
sub     $0x10,$rsp

としています。
これは、スタックポインタ(rsp)をベースポインタ(rbp)に代入してから、
スタックポインタ(rsp)から、16をひいています。
この操作で、この関数で使える16バイトのワークエリアを確保しています。

4バイトずつに分けるならば、
-0x4(%rbp), -0x8(%rbp), -0xC(%rbp), -0x10(%rbp)
の四つのintの領域が確保されている感じです。

9という値をiに代入しているので、そのiが-0x8(%rbp)に確保
されている様にみえますね。

次にprintfを呼びだす為の準備を行っている様ですが、
lea     0x47(%rip),%rdi
で、rdiレジスタに "%d" という文字列のポインタを入れている様です。

つぎの、
mov     $0x0,-0x4(%rbp)
ですが、この処理では一見意味が無いように見えます。
おそらく、-0x4(%rbp)をクリアしておき、処理によっては
そこを使う事があるのかな?と思います。

その次の、
mov      -0x8(%rbp),%esi
mov      $0x0,al
ですが、先ほど代入した9をediに入れていて、alに0を入れています。
この事から、printf関数の第一引数は rdi, 第二引数は、ediに入れて呼ぶ
必要があるようですね。

alレジスタを0にしている理由はわかりません。
このコンパイラの関数呼び出しのルールでalを0にする必要があるのかも
しれません。

コンパイラが出力するコードは、コンパイラ屋さん(人間)の考たコードです。
場合によっては、無駄な(に見える)コードが出てくる事もあります。

ただ、色々な場合に対応するために、0でクリアする処理が入っているのでは
ないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • C

    4543questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • アセンブリ言語

    116questions

    アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。