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

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

ただいまの
回答率

89.72%

cソースをアセンブリ言語に

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 638

nezu511

score 7

 c言語で書いた以下のコードをアセンブリコードで書きたい

c言語で書かれたプログラムと同じ動作をするアセンブリコードを書きたいです。
cのソースファイルをGASに変換しアセンブリコードを作成しました。
そのgasコードを参考にしながら同じような動作をするアセンブリコードを自分で書こうとしたのですがどうも望んだように動作せず質問しました。

 聞きたいこと

1.gccが吐いたアセンブリコードの
     movl stdout@GOT(%ebx), %eax
     movl (%eax), %eax
という部分は、メモリ領域のstdout@GOtの値を%eaxに渡しているという認識で正しいですか?
また関数fflushはstdoutのファイルストーリームが引数だと認識しているのですが、stdout@GOTという領域に格納されているメモリを示す値にファイルストリームが入っているという認識で正しいでしょうか?

2.
    movl    stdout@GOT(%ebx), %eax
    movl    (%eax), %eax
この部分はgccが吐いたコードを自作コードでそのまま同じように参考にしているのですが、自作コードではここでエラーがでてコアダンプしてしまいます。
一体なぜエラーが起こるのでしょうか?

 エラーメッセージ

エラーメッセージはSegmentation fault (コアダンプ)

 もとになるC言語のプログラム

#include<stdio.h>
#include<unistd.h>  // sleep()関数のためのライブラリ。

int main(void)
{
  int i;
  printf("No. 1");
  for(i=2; i<=8; i++){
  fflush(stdout);
  sleep(1);  // 次の処理に移る前に1秒待つ。これをしないとすぐに表示が切り替わる。
  printf("\rNo. %d",i);
}
  printf("\n");

  return 0;
}

 gccに-Sオプションで吐かせたGAS(必要な部分のみ)

.L3:
    movl    stdout@GOT(%ebx), %eax
    movl    (%eax), %eax
    subl    $12, %esp
    pushl    %eax
    call    fflush@PLT
    addl    $16, %esp
    subl    $12, %esp
    pushl    $1
    call    sleep@PLT
    addl    $16, %esp
    subl    $8, %esp
    pushl    -12(%ebp)
    leal    .LC1@GOTOFF(%ebx), %eax
    pushl    %eax
    call    printf@PLT
    addl    $16, %esp
    addl    $1, -12(%ebp)

 上を参考に自分で書いたGAS

.data
test:  .string "%d"
test2: .string "\r%d"

.text
.globl main
main:
      pushl %ebp
      movl %esp, %ebp
      subl $32, %esp
      xorl %ebx, %ebx
      movl %ebx, 4(%esp)
      movl $test, (%esp)
      call printf

main2:
      inc %ebx
      cmpl $20, %ebx
      je programend
      movl stdout@GOT(%ebx), %eax
      movl (%eax), %eax
      movl %eax, (%esp)
      call fflush
      movl $1, (%esp)
      call sleep
      movl %ebx, 4(%esp)
      movl $test2, (%esp)
      call printf
      jmp main2

programend:
      leave
      ret

 gccが吐いたgasコードフル

    .file    "main.c"
    .text
    .section    .rodata
.LC0:
    .string    "No. 1"
.LC1:
    .string    "\rNo. %d"
    .text
    .globl    main
    .type    main, @function
main:
.LFB0:
    .cfi_startproc
    leal    4(%esp), %ecx
    .cfi_def_cfa 1, 0
    andl    $-16, %esp
    pushl    -4(%ecx)
    pushl    %ebp
    .cfi_escape 0x10,0x5,0x2,0x75,0
    movl    %esp, %ebp
    pushl    %ebx
    pushl    %ecx
    .cfi_escape 0xf,0x3,0x75,0x78,0x6
    .cfi_escape 0x10,0x3,0x2,0x75,0x7c
    subl    $16, %esp
    call    __x86.get_pc_thunk.bx
    addl    $_GLOBAL_OFFSET_TABLE_, %ebx
    subl    $12, %esp
    leal    .LC0@GOTOFF(%ebx), %eax
    pushl    %eax
    call    printf@PLT
    addl    $16, %esp
    movl    $2, -12(%ebp)
    jmp    .L2
.L3:
    movl    stdout@GOT(%ebx), %eax
    movl    (%eax), %eax
    subl    $12, %esp
    pushl    %eax
    call    fflush@PLT
    addl    $16, %esp
    subl    $12, %esp
    pushl    $1
    call    sleep@PLT
    addl    $16, %esp
    subl    $8, %esp
    pushl    -12(%ebp)
    leal    .LC1@GOTOFF(%ebx), %eax
    pushl    %eax
    call    printf@PLT
    addl    $16, %esp
    addl    $1, -12(%ebp)
.L2:
    cmpl    $8, -12(%ebp)
    jle    .L3
    subl    $12, %esp
    pushl    $10
    call    putchar@PLT
    addl    $16, %esp
    movl    $0, %eax
    leal    -8(%ebp), %esp
    popl    %ecx
    .cfi_restore 1
    .cfi_def_cfa 1, 0
    popl    %ebx
    .cfi_restore 3
    popl    %ebp
    .cfi_restore 5
    leal    -4(%ecx), %esp
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size    main, .-main
    .section    .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
    .globl    __x86.get_pc_thunk.bx
    .hidden    __x86.get_pc_thunk.bx
    .type    __x86.get_pc_thunk.bx, @function
__x86.get_pc_thunk.bx:
.LFB1:
    .cfi_startproc
    movl    (%esp), %ebx
    ret
    .cfi_endproc
.LFE1:
    .ident    "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
    .section    .note.GNU-stack,"",@progbits

 回答よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

「movl stdout@GOT(%ebx), %eax 」という命令が読みだすメモリのアドレスは、stdout@GOT(%ebx) で指定されています。つまり

  • メモリアドレス = %ebx レジスタの値 + "stdout@GOT" シンボルの値

です。%ebx レジスタの値が異なれば、アクセスするアドレスが異なり、結果も異なります。ちなみに、"stdout@GOT"の値はリンク時に決定するのでしょう。

   movl    stdout@GOT(%ebx), %eax
   movl    (%eax), %eax
gccが吐いたコードをそのまま同じようにしているのですが、一体何故エラーが?

「movl stdout@GOT(%ebx), %eax」が同じ結果になるには、コードの字面を同じにしただけではダメで、その時の %ebx レジスタの値も同じでなければなりません(逆に、動作の意味するところが同じならコードの字面は違っても構わない)。
ただし、%ebx にセットされるアドレスは実行するつど異なる可能性があるので、ただ単に同じ値にすれば良いのではなく、「レジスタの値を、同じ役割のメモリアドレスにする」とでも言うべきですが、
貴方のコードと、gccが吐いたコードと、%ebx の値・役割は同じですか?

コンパイラが生成したコードを先頭から見ていくと、貴方が注目していないと思われる部分に、%ebx に値をセットしている部分が見つかります。次の2行です。

    call    __x86.get_pc_thunk.bx
    addl    $_GLOBAL_OFFSET_TABLE_, %ebx


この後 %ebx はこの値であることを前提として、あのmovl命令があります。

一方、貴方のコードは %ebx をカウンタとして使い、値は 0〜20 と変化します。結果、あのmovl命令は0番地付近をアクセスすることになるので、コアダンプは当たり前です。
アドバイスを3点。

  • %ebx に gcc と同じ手順で値をセットしてから使う
  • カウンタには %ecx を使ったらどうか
  • %ebx, %ecx は、関数先頭部分(プロローグ部)でプッシュし、リターン直前(エピローグ部)でポップする

特に3つ目にご注意。コンパイラが生成したコードは、%ebx, %ecx を呼ばれた時点の元の値に戻しているので、貴方のコードもそうすべきことがわかります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/12 12:02

    メモリアドレスの計算の方法を勘違いしていました。
    call __x86.get_pc_thunk.bx
    addl $_GLOBAL_OFFSET_TABLE_, %ebx
    この2つの操作にこのプログラムの肝だとおもいもしませんでした。
    相談しなければいつになっても解決しなかったと思います。
    アドバイスどおりに修正してみると自分がしてほしかった動作をしました。
    本当にありがとうございました。

    キャンセル

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

  • ただいまの回答率 89.72%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる