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

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

ただいまの
回答率

89.65%

アセンブリ言語の基本part3

解決済

回答 2

投稿 編集

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

strike1217

score 579

細々とした質問なので、複数質問することをお許しください。

アセンブリ言語で読めない箇所があります。
以前の質問で、以下のような例を示してもらいました(ありがとうございます。)

        .section .rodata
xx:     .quad   subr
        .text
subr:   inc     %rax           # ++rax
        ret
        .global aFunc
aFunc:
        mov     $0, %rax

        call    subr           
        call    (subr)         

        call    *(xx)          # 1
        call    *xx            # 2

        mov     $subr, %rdx    # 3
        call    *%rdx          # 

        lea     subr(%rip), %rdx 
        call    *%rdx          

        lea     xx(%rip), %rdx 
        call    *(%rdx)       
        ret

ですが、私の環境ではうまく行きませんでした。
1、2、3番と出来なかった所には番号が振ってあります。
(私にとってはこの3つができない方が納得いきます。)

1、call    *subrとできない。
subrは絶対アドレスです。 なぜでしょうか?
1番,2番が仮にも出来る環境なら、call *subr これはできないとおかしいのではないでしょうか?

2,call    subr  及び call    (subr)
の結果が変わらないのは、ラベルに対して括弧を付けても意味が無いということですよね?
(付けても付けなくてもおk)

3,$subr は$は即値に付ける構文ですよね?
ラベルにも付けれるんですか?
(私の環境ではできませんでした。)

4,以上を見ていただければ、わかりますが・・・
アスタリスクは、絶対アドレスが入っているレジスタに対する構文ではないでしょうか?
ラベルに[*]を付ける方が納得がいかないんですが・・・・
(レジスタ以外に対するアスタリスクはすべてダメでした。)

5,もう1つ気になる事があります。

  movq    -8(%rbp), %rax
    movabsq    $7812735413947559801, %rdx
    movq    %rdx, (%rax)
    movabsq    $7308533390257515808, %rcx
    movq    %rcx, 8(%rax)


calloc関数を使って、ヒープを確保しその領域にstrcpy()で文字列を代入します。
しかしこれはオーバーフローではないでしょうか??

代入している即値が「8Byte」を完全に超えています。
64bitのレジスタに対してはオーバーフローです。
しかし、正常に動作しています・・・なぜでしょうか?

6, mov  $1, %rax
最後に、qやl と言った文字をニーモニックにつけますが、
これは即値によって決めるものですか?それともレジスタの容量によって決めるものですか?
あ、言い方が悪かったです。ソースとデステネイションのどちらによって決めるものですか?

7,lea  xx(%rip), %rdx    |    call    *(%rdx)  
これを1つにして、call *xx(%rip) と記述できます。(正常に実行できます。)

これも変です。
xx(%rip) の中身はアドレスです。
そのアドレスが指す領域内のアドレスが関数のポインタです。

なら、イメージ的には、call *(xx(&rip))こんな感じになります。
実際には文法エラーですが、call *xx(%rip)の記述はおかしいような気がするんですが・・・
これでは、%rip + xx のアドレスを絶対アドレスとしてcallしてしまっているように見えるのですが・・・・
わざわざ、オフセット付きの括弧となしの括弧の使い方を区別しているのに、これでは意味が無くなってしまいます。

[環境]
64bit Linux Debian系 intel CPU です。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • rubato6809

    2017/07/14 19:58

    私はUbuntu 16.04で動作確認しましたし、Linux Mint(バージョンは?)で動作したんでしょ。動く環境があることを告知しないとダメでしょ。

    キャンセル

  • strike1217

    2017/07/14 20:26

    あ、すいません。Linux mintでは動きますね。Linux mintの方はGDBの動作が変なのでデバッグしていません。Linux mintの方とrubato6809さんの方では出来ているようなので、古いバージョンだとできるのかな?・・・予測です。

    キャンセル

  • strike1217

    2017/07/14 20:36

    自分が理解できないのは文法の方ですね。

    キャンセル

回答 2

checkベストアンサー

+1

3,$subr は$は即値に付ける構文ですよね?

Yes. 

ラベルにも付けれるんですか?

mov $subr, %rdxは、シンボル"subr"の値を%rdxにロードするのです。できて当たり前でしょう。念の為に言うと、シンボル値が決定するのはリンク時である。
おまけ:ら抜き言葉は止めよう。軽薄に聴こえるw
×付けれる
○付けられる

私の環境ではできませんでした

ひとつ見てみたいのは、君の手元で、アセンブルリストを出して、mov $subr, %rdxの部分がどうなってるか、(エラーにならない、と言うなら)何バイト命令としてアセンブルされるか。
もうひとつは、movq $subr, %rdxとしたら、何か変化があるか。。。

5. 代入している即値が「8Byte」を完全に超えています

って
movabsq    $7812735413947559801, %rdx
movabsq    $7308533390257515808, %rcx
のことであれば、strike君の名誉のために、さっさと質問を取り下げるのが
良いと思う。

6, mov  $1, %rax
最後に、qやl と言った文字をニーモニックにつけますが、
これは即値によって決めるものですか?それともレジスタの容量によって決めるものですか?

0x01も、0x0001も、0x00000001も、0x00000000000000001 も、値は全て1ですが、デスティネーションとして %rax が指定されてるのだから、その1は64ビットの1である(LSBitのみ1で、それ以外の63bitは0である)と判断できます。
一般的な言い方をすれば「デスティネーションとして指定されたレジスタのビット数(「容量」でも構わないよ、普通そういう言い方しないと思うが)によって決まる」と、容易に想像がつく話だけどねえ。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/15 20:23

    ダイナミックリンカが再配置を行えない理由がわかりませんが、
    おそらく論理アドレスの低い領域にロードするようにすればできるかもしれません。
    それは、また後ほど!!

    ・・・はぁ。
    それで・・・次は文法の方ですね。

    キャンセル

  • 2017/07/15 20:29

    > mov $0x0, %rdx
    > callq *%rdx
    > この場合は、デスティネーションがrdxなのに、ニーモニックにqは付かないんです?

    脊髄反射w

    ・・・・
    ちょっと本当にわからないです。
    なんでqが付かないんです?

    キャンセル

  • 2017/07/20 09:26

    落ち穂拾いw

    qの有無で何が違うか、まだわからないなら call/callq、mov/movqを実際にアセンブルして、アセンブルリストかobjdumpで逆アセンブルするか、どんな機械語になるか、確認してみると良いよ。たった4行じゃないか、自分の手を動かすことが大事。

    キャンセル

0

Linux mint でcall *subrとやってみましたが、segmentation faultです。
もぉ~~。わけわからん。

全てのパターンを暗記するしかないのかな?
まとめてみました。

オスセットなし括弧 オフセットあり括弧 括弧なし+[*] オフセットなし括弧+[*] オフセットあり括弧+[*]
(%rax) -8(%rbp) call *%rax call *(%rax) call *xx(%rip)
ポインタの要素   アドレス+オフセット  ポインタ  ポインタのポインタ   ポインタのポインタ

5番目は、相対アドレッシングなのに、*を付けています。
絶対アドレスに付けるものじゃないの?と思いますが・・・おそらくポインタが指すメモリ領域のアドレスが絶対アドレスだからという屁理屈ですかね?(たぶん)

call *xx(%rip) はポインタかと思っていました。
なぜなら、xx(%rip)は、XXを加算したアドレスだからです。
組み合わせによって意味が全然違う・・・・

$や*はレジスタ以外にも使用可能な構文。
()は、レジスタに対する構文で、ラベルには付けても付けなくてもおk・・・って感じですかね。
call *subrができないという事はおそらく・・・

label (label) $label *label call *$label
ポインタ labelと変わらない ラベルを即値とする(?) ポインタのポインタ  X (*なしでも無理)

またラベルはオフセットにも絶対アドレスにも使用可能!

という事ですかね?(間違っていたらご指摘をお願いします。)

「私の環境では、$や*はレジスタに対して使用可能な構文」となっていました。
こちらの方が納得ですが・・・
私の環境(バージョンが新しい)

label (label) $label *label
ポインタ labelと変わらない  X   X 

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/17 22:28 編集

    > これ以上の説明はできません

    そうか。くどいと思うだろうが、読んで・図に描いて・トレースしてみてほしい。

    xx も subr も、ラベルです。どちらのラベルも、メモリアドレスが値です。
    call *xx は "callq *0x400ae0" 即ち ff 14 25 e0 0a 40 00、
    call *subr は "callq *0x400ad8" 即ち ff 14 25 d8 0a 40 00、
    どちらも機械語には、xx と subr の、アドレス(絶対番地)である、0x400ae0と0x400ad8がエンコードされている事を確認しよう。

    call *xx = "callq *0x400ae0" の場合。
    0x400ae0番地から、メモリには 0xd8 0x0a 0x40 0x00 0x00 0x00 0x00 0x00 という値が並んでいる。
    "callq *0x400ae0"命令によって、
    1. %rip の値をスタックにプッシュする(CALL命令だから)
    2. <メモリから8バイトの値を読み出す>。
    (リトルエンディアンなので)読み出した値は 0x0000000000400ad8である。
    3. <メモリから読み出した値 0x400ad8 を%ripに代入する>。
    ここまでが、"call *xx"命令の動作。
    4. CPUは、0x00400ad8番地のメモリにある、0x48 0xff ...を
    (次の命令として)実行しようとする。
    このバイト列は inc %rax 命令であり、さらにret命令が続くので、もちろん問題なく動作する。

    call *subr = "callq *0x400ad8" の場合。
    0x400ad8番地から、メモリには 0x48 0xff 0xc0 0xc3 0x00 0x00 0x00 0x00 という値が並んでいる。
    "call *subr"命令によって、
    1. %rip の値をスタックにプッシュする(CALL命令だから)。
    2. <メモリから8バイトの値を読み出す>。読み出した値は 0x00000000c3c0ff48である。
    3. <メモリから読み出した値 0xc3c0ff48 を%ripに代入する>。
    ここまでが"call *subr"命令の動作。
    4. CPUは、0xc3c0ff48番地のメモリに命令があるものとして読み出そうとする・・・が、その番地のメモリは、このプロセスに割当られていない。よって、Segmentation faultが起こる。

    なぜメモリが割り当てられていないと言えるか。Segmentation faultが起きたから(笑)。
    少しだけ検証すると、objdump -d a.out が表示する機械語命令の最後は
    0000000000489844 <_fini>:
    489844: 48 83 ec 08 sub $0x8,%rsp
    489848: 48 83 c4 08 add $0x8,%rsp
    48984c: c3 retq

    "-static"でスタティックリンクしたのだから、ユーザステートで動作する機械語はここまでなんじゃないのかな?他に、スタックや静的データ領域(グローバル又はstaticな変数領域)も割当られているけど、0xc3c0ff48番地は桁が違い、カスりもしないアドレスのようだ・・・。

    もし、仮に subr 番地から読み出した8バイトをアドレスとして見た場合、
    ・このプロセスに割り当てられたメモリを指している
    ・OSが、そのメモリ領域を「実行可能領域」に設定してある
    ・そのアドレスから始まるメモリのバイト列が、retで終わる、実行可能な機械語列である
    という条件が成り立つなら動作するのだから、絶対に動作しないとは言い切れない。でもそれは奇跡です。

    キャンセル

  • 2017/07/17 23:24 編集

    関数内のプログラムがアドレスとして解釈されているんですね!

    大変分かりやすいです!!
    アセンブリ言語の文法(括弧について)は再度質問するかもしれません(しなかもしれないです)
    長々付き合っていただきありがとうございます。

    キャンセル

  • 2017/07/17 23:45 編集

    > 関数内のプログラムがアドレスとして解釈されている

    そういうこと。
    ・「call *ラベル」という形式だけで決まるのではない事。
    ・ひとつひとつの機械語の動作は、極めて単純。「call *ラベル」は、ちょっと複雑な部類か。プログラマの思惑や思い込みに合わせてくれる…なんてことはなく、決められた通りに、機械語をアドレスと見ることだってやる。

    不可解なバグを調査するときは、人間の思い込みを排除して、CPU(機械)になったつもりでコードを調べる必要がある。
    「神は細部に宿る」・・・コードの流れを大雑把に把握することも大事だが、ひとつひとつの動作の、隅っこの細部まできちんと理解しておくことも大事である。

    キャンセル

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

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

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