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

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

ただいまの
回答率

90.03%

アセンブリ出力と逆アセンブルのプログラムの比較について。

受付中

回答 8

投稿

  • 評価
  • クリップ 0
  • VIEW 1,852

carnage0216

score 131

以下のC言語をアセンブリプログラムとオブジェクトプログラムから逆アセンブルしました。

/* hoge.c */
  int func(int n)
  {   
      int a = 2;
      int b = 3;
      int ret;

      if(n == 0){ 
          ret = a + b;
      }else{
          ret = a * b;
      }

      return ret;
 }

こちらがアセンブリプログラムです。

.text
    .globl    _func
    .def    _func;    .scl    2;    .type    32;    .endef
_func:
LFB0:
    .cfi_startproc
    pushl    %ebp     #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp     #,
    .cfi_def_cfa_register 5
    subl    $16, %esp     #,
    movl    $2, -8(%ebp)     #, a
    movl    $3, -12(%ebp)     #, b
    cmpl    $0, 8(%ebp)     #, n
    jne    L2     #,
    movl    -8(%ebp), %edx     # a, tmp93
    movl    -12(%ebp), %eax     # b, tmp94
    addl    %edx, %eax     # tmp93, tmp92
    movl    %eax, -4(%ebp)     # tmp92, ret
    jmp    L3     #
L2:
    movl    -8(%ebp), %eax     # a, tmp96
    imull    -12(%ebp), %eax     # b, tmp95
    movl    %eax, -4(%ebp)     # tmp95, ret
L3:
    movl    -4(%ebp), %eax     # ret, _7
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE0:
    .ident    "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"

こちらが逆アセンブルしたプログラムです。

Disassembly of section .text:

00000000 <_func>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 10                sub    $0x10,%esp
   6:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%ebp)
   d:   c7 45 f4 03 00 00 00    movl   $0x3,-0xc(%ebp)
  14:   83 7d 08 00             cmpl   $0x0,0x8(%ebp)
  18:   75 0d                   jne    27 <_func+0x27>
  1a:   8b 55 f8                mov    -0x8(%ebp),%edx
  1d:   8b 45 f4                mov    -0xc(%ebp),%eax
  20:   01 d0                   add    %edx,%eax
  22:   89 45 fc                mov    %eax,-0x4(%ebp)
  25:   eb 0a                   jmp    31 <_func+0x31>
  27:   8b 45 f8                mov    -0x8(%ebp),%eax
  2a:   0f af 45 f4             imul   -0xc(%ebp),%eax
  2e:   89 45 fc                mov    %eax,-0x4(%ebp)
  31:   8b 45 fc                mov    -0x4(%ebp),%eax
  34:   c9                      leave
  35:   c3                      ret
  36:   90                      nop
  37:   90                      nop

あの扱う値は同じなのにmovlの部分が逆アセンブリではmovになっていたりしますが、これはどちらのアセンブリプログラムが正しいというかちゃんとコードとして良いものなのでしょうか?
movlは4バイトの値を扱う際の命令ですが、逆アセンブリではmovとなっています。データや数値の扱いなどでバイトやbitは正確な方が良いと思うのでどうかと思うのですが。
あるいは逆アセンブリのほうの最初で

   6:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%ebp)
   d:   c7 45 f4 03 00 00 00    movl   $0x3,-0xc(%ebp)


と数値$0x2と$0x3を4バイトとして扱うよとmovlで各レジスタへコピーしたため、今後のレジスタでも4バイトとして扱われるためわざわざmovlとは書かず、mov命令となっているのかなと素人の私なりに考えてみましたが実際はどうなのかいまいちわかりません。
なぜ逆アセンブルすると変わるのでしょうか?またどっちのアセンブリソースのほうが良い?というか正確なものなのでしょうか?
どうかよろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 8

+5

逆アセンブルの方は、必要のないサフィックスを付けていないだけでは?

movl $0x2,-0x8(%ebp)は付けないとサイズが分からない。
mov -0x8(%ebp),%edxは付けなくてもサイズが確定している。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/08 19:32

    回答どうもありがとうございます。
    movl $0x2,-0x8(%ebp)のmovlは扱うデータ量がわからないので4バイトのデータで扱ってしまおうという考えでmovlを使ったのでしょうか?
    それとは違いmov -0x8(%ebp),%edxはmovにデータ量の情報を付けなくてよいのでサイズが確定しているということですか?
    ただ、mov -0x8(%ebp),%edxのどこを読んでサイズが確定しているというかサイズがわかったかはわかりませんが。

    キャンセル

  • 2018/03/08 20:13

    代入するのが32ビットレジスタでしょ?

    キャンセル

  • 2018/03/08 20:21

    > 扱うデータ量がわからないので4バイトのデータで扱ってしまおうという考えでmovl

    違います。
    この「2」という値は int a = 2; の「2」ですよ。「a」という変数を -8(%bp) から始まる4バイトに割り当てた、なぜ4バイトなのかといえば、int型だから。
    2という値を4バイトのメモリに格納するから、movl としなければならなかった。

    > mov -0x8(%ebp),%edxのどこを読んでサイズが確定している

    オペランドに %edx があるから4バイトのデータ転送だと確定する。

    キャンセル

  • 2018/03/08 20:32

    どうもありがとうございます。

    キャンセル

0

正しさってなんでしょうね……

とりあえずintel資料によるとOpcode C7の命令は
MOV r/m16, imm16
MOV r/m32, imm32
のどちらかです。

よってmovの方が正しいような気もしますが
gnu assemblerに対する正しい入力としてはmovlなのでしょう。

機械語とアセンブリ言語が別な以上アセンブラ次第で、どちらが正しい命令かは変化します。

ちなみに、機械語とアセンブリ言語が別な例として0x90が挙げられます。
機械語としてはxchg eax, eaxです。アセンブリ言語としてはnopです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

こんにちは。

回答依頼が届いていましたので来ました。
私自身はPC用CPUのアセンブラを使った経験は僅かしか無いので具体的なことは分かりません。
ただ、「言語」全般に言えることですが、同じことを表現するのに複数の表現方法があるのは一般的と思います。「プログラミング言語」は曖昧さを嫌うので比較的表記の揺れは少ないですが、全く無いわけではありません。そのような表記の揺れではないかと思います。

ご提示されている表記の差は「センター」と書くのか「センタ」と書くのか程度の差異のように感じます。
つまり、Cコンパイラのアセンブリ出力の開発者と逆アセンブラ開発者のセンスの差であり、かつ、それらの揺れを統一する作業が行われていない、もしくは行われていても漏れたというだけだろうと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

 プログラムの正しさとはそもそもなんですか?

「元のC言語のソースを適切に表現する」という意味ならば答えは存在しません。C言語においてはint型の具体的なバイト数が定められていないからです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

fuzzballさんが回答された通りです。
逆アセンブラは必要のないサフィックスをつけずに表示しています。
そして、GNUアセンブラにとって、movもmovlも、どちらも正しい。

質問者は手元に、コンパイラが出力したアセンブリコードがあり、アセンブラ(as)もあるのだから、アセンブリコードに手を加えてアセンブルするだけで確かめられるはずです、こんなふうに。

$ as -a hoge.s
  (途中省略)
  23 0036 89C3          movl    %eax, %ebx  # 32bit
  24 0038 89C3          mov     %eax, %ebx  # 32bit
  25 003a 6689C3        movw    %ax, %bx    # 16bit
  26 003d 6689C3        mov     %ax, %bx    # 16bit
  27 0040 88C3          movb    %al, %bl    # 8bit
  28 0042 88C3          mov     %al, %bl    # 8bit
  29                  
  30 0044 8945FC        movl    %eax, -4(%ebp)  # 32bit
  31 0047 8945FC        mov     %eax, -4(%ebp)  # 32bit
  32 004a 668945FC      movw    %ax, -4(%ebp)   # 16bit
  33 004e 668945FC      mov     %ax, -4(%ebp)   # 16bit
  34 0052 8845FC        movb    %al, -4(%ebp)   # 8bit
  35 0055 8845FC        mov     %al, -4(%ebp)   # 8bit
  36                  
  37 0058 C745F802      movl    $2, -8(%ebp)    # 32bit
  37      000000
  38 005f 66C745F8      movw    $2, -8(%ebp)    # 16bit
  38      0200
  39 0065 C645F802      movb    $2, -8(%ebp)    # 8bit

オペランドに書かれたレジスタ名で処理サイズが決まる場合は、movとmovl、どちらも同じ機械語にアセンブルされることがわかります。「手と頭を動かす」とは、例えばこういうこと。

さらに、サフィックスの無い  mov $2, -8(%ebp)  という行をアセンブルしたらどうなるか、質問者ご自身の手で確かめてください。

ご存知と思いますが、 $ cc hoge.c  の代わりに $ cc hoge.s  でも実行ファイル(MinGWだから a.exe)が作られ、もちろん実行可能です。バグがあれば吹っ飛びます笑。先日お答えした「コンパイラにアセンブリ言語を出力させて雛形・お手本にします」とは、このこと。躊躇せず、どんどん手を入れて、いろいろ試してみることですね。
Enjoy!

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

昔、8086アセンブラを作ることになるかもしれないということで、Intel記法を調べてみたことがあります。

Intel記法では、参照するデータがWORDなのかBYTEなのかなどについて、データセグメントに定義している情報が必要となります。Intel記法として、これらデータセグメントの定義がコードセグメントに前に必ずなければならないのであれば、問題ないのですが、そうでないとすると、一度全部のソースを調べて各データのタイプ(WORDかBYTEか)を調べてから、コードセグメントのニューモニックを機械語に変換することが必要となります。たしか、データのタイプによっては、命令コードのサイズが変わったかと思います。このため、確実に翻訳するには、2回アセンブラソースを走査する必要があり、アセンブルに時間がかかってしまいます。逆にデータのタイプがWORDなのに、BYTEでアクセスするようなミスを防ぐことができます。

一方、AT&T方式では、アセンブラソースを上から順に操作するだけで、ニューモニックからWORD用の命令なのか、BYTE用の命令なのか即座にわかるので、そのまま機械語に変換することができます。あとは、アドレス部分の書き方ですが、これは、一連の作業が終わってから芋づる式にポインターを使って書き直せべ簡単に機械語翻訳が可能となります。

つまり、インテル方式では、プログラムミスを避ける可能性が高くなりますが、アセンブルに時間がかかる。逆にAT&T方式では、プログラムのチェックは甘くなるがアセンブルは高速にできるということになります。

昔は、ソースコードを何回も走査と時間がかかると言うことで、できるだけ走査回数を少なくすることが良しとされていました。とくに、コンパイラでもPascalなどのワンパスコンパイラ(一回走査するだでコンパイル)が有名です。詳しくは、
https://ja.wikipedia.org/wiki/コンパイラ#ワンパスとマルチパス
に書かれています。
また、アセンブラについてもいいWikiを見つけました。以下のリンクを見てください。
https://ja.wikipedia.org/wiki/アセンブリ言語#アセンブラ
参考になると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/15 12:57

    diracpaulさん

    word ptrのようなサイズ指定は省略可能ですから、省略した際はアセンブラが適当に判断して補完してくれるでしょう。`dw`で確保した領域はword ptrとみなしてくれるかもしれません(試していないので確かなことは言えませんが)。

    ただ、それは「そういう書き方もできる」というだけの話であって、あなたが回答に書かれているような制限がないことは先にコメントした通りですし、Intel記法とAT&T記法でできることに違いはありません(あったら困ります)。

    ちなみに、as(GNU Assembler)は元々はAT&T記法だけでしたが、現在はIntel記法にも対応しています。

    キャンセル

  • 2018/03/15 18:12

    最初にインテルのアセンブラ仕様を見て気になったのは、MOVS,STOS,SCASです。これらのコマンドは、レジスタが決まっているので、オペランドなしが可能です。しかし、word 単位で操作したり、byte単位で操作することにが可能です。仕様からは.diレジスタやsiレジスタが指すアドレスのタイプをアセンブラで解釈するようにおもわれました。でも、実際には、word
    ptrやbyte ptrでコーディングしているのかも知れませんね。

    キャンセル

  • 2018/03/15 18:22

    オペランドなしの場合は後ろにサイズを付けます。
    movsb, movsw, movsd

    キャンセル

-1

扱う値は同じなのにmovlの部分が逆アセンブリではmovになっていたりしますが、これはどちらのアセンブリプログラムが正しいというかちゃんとコードとして良いものなのでしょうか?

x86のアセンブラには、メジャーな書き方としてIntel記法とAT&T記法があります。

  • Intel記法…命令はMOVとかPUSHとか幅指定なしで、レジスタの幅で表現する(どうしようもないときはDWORD PTRのようなものを入れる)
  • AT&T記法…movlpushwのように、命令に幅指定を入れる

「どちらが正しい」というものではなく、共存しています。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/08 09:56

    どちらもAT&T記法ではないですか?

    キャンセル

  • 2018/03/08 10:27

    たしかに、オペランドの順序はAT&Tですね…こんな折衷的な書き方は初めて見たので、ちょっと頓珍漢になってしまったかもしれませんね。

    キャンセル

  • 2018/03/08 10:29

    レジスタ名に%付いてますし。

    キャンセル

-1

x86アセンブリのニーモニックにはIntel記法とAT&T記法があり、マイクロソフトはIntel記法を使っていますが、gnu系のツールでは、なぜかAT&T記法が使われています(ライセンスの問題? その辺はよく判りません)。

なぜ逆アセンブルすると変わるのでしょうか?

AT&T記法では命令の後ろに扱うデータサイズに応じたサフィックス(b,s,w,l,qなど)を付けることになっていますが、レジスターも名前でデータサイズを区別しており、レジスターに対する操作はレジスター名でデータサイズが決まるため、わざわざサフィックスを付けなくても判るだろうということで省略しているのではないかと思います。

またどっちのアセンブリソースのほうが良い?というか正確なものなのでしょうか?

私はIntel記法でしか勉強したことがないので、AT&T記法の作法についてはよく判りません。サフィックスを付けたり付けなかったりでは統一感がないので、自分で書く際は付けた方が良いような気はします。

というか、当たり前ですが、インテルが提供しているCPUマニュアルはすべてIntel記法で書かれていますし、市販の解説書もほぼIntel記法ですので「そもそもAT&T記法ではなくIntel記法の方が良い」というのが私の見解です。

gcc(g++)では、-Sオプションに加えて-masm=intelオプションを付けるとアセンブリソースがIntel記法になります。
gdbではset disassembly-flavor intelコマンドで逆アセンブルソースがIntel記法になります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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