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

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

新規登録して質問してみよう
ただいま回答率
85.50%
アセンブリ言語

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

C

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

Q&A

解決済

1回答

2467閲覧

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

nezu511

総合スコア9

アセンブリ言語

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

C

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

0グッド

1クリップ

投稿2018/08/11 20:42

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言語のプログラム

c言語

1#include<stdio.h> 2#include<unistd.h> // sleep()関数のためのライブラリ。 3 4int main(void) 5{ 6 int i; 7 printf("No. 1"); 8 for(i=2; i<=8; i++){ 9 fflush(stdout); 10 sleep(1); // 次の処理に移る前に1秒待つ。これをしないとすぐに表示が切り替わる。 11 printf("\rNo. %d",i); 12} 13 printf("\n"); 14 15 return 0; 16}

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「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行です。

gas

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

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

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

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

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

投稿2018/08/12 02:37

rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nezu511

2018/08/12 03:02

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問