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

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

ただいまの
回答率

89.20%

アセンブリ言語で読めない箇所があります。

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 2,841

strike1217

score 583

ポインタを使用するプログラムです。

  1 #include<stdio.h>
  2 #include<ctype.h>
  3 
  4 char *test(char *buf){
  5         char *p;
  6         int c;
  7         p = buf;
  8 
  9         while((c = *p) != 0){
 10                 if(islower(c))
 11                         *p = (char)toupper(c);
 12                 p++;
 13         }
 14         return buf;
 15 }
 16 
 17 int main(){
 18         char s[] = "you will make me happy\n";
 19         printf("%s\n", test(s));
 20 
 21         return 0;
 22 }
 23 


コンパイルしたものを書きます。

    .file    "pointer_arg.c"
    .text
    .globl    test
    .type    test, @function
test:
.LFB2:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movq    %rdi, -24(%rbp)
    movq    -24(%rbp), %rax
    movq    %rax, -8(%rbp)
    jmp    .L2
.L4:
    call    __ctype_b_loc@PLT
    movq    (%rax), %rax
    movl    -12(%rbp), %edx
    movslq    %edx, %rdx
    addq    %rdx, %rdx
    addq    %rdx, %rax
    movzwl    (%rax), %eax
    movzwl    %ax, %eax
    andl    $512, %eax
    testl    %eax, %eax
    je    .L3
    movl    -12(%rbp), %eax
    movl    %eax, %edi
    call    toupper@PLT
    movl    %eax, %edx
    movq    -8(%rbp), %rax
    movb    %dl, (%rax)
.L3:
    addq    $1, -8(%rbp)
.L2:
    movq    -8(%rbp), %rax
    movzbl    (%rax), %eax
    movsbl    %al, %eax
    movl    %eax, -12(%rbp)
    cmpl    $0, -12(%rbp)
    jne    .L4
    movq    -24(%rbp), %rax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE2:
    .size    test, .-test
    .globl    main
    .type    main, @function
main:
.LFB3:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movabsq    $7812735413947559801, %rax
    movq    %rax, -32(%rbp)
    movabsq    $7308533390257515808, %rax
    movq    %rax, -24(%rbp)
    movabsq    $2948273595836448, %rax
    movq    %rax, -16(%rbp)
    leaq    -32(%rbp), %rax
    movq    %rax, %rdi
    call    test
    movq    %rax, %rdi
    call    puts@PLT
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE3:
    .size    main, .-main
    .ident    "GCC: (Debian 6.3.0-18) 6.3.0 20170516"
    .section    .note.GNU-stack,"",@progbits

test()に入ったあとなんですが、、、、
-24(%rbp) = buf
-8(%rbp) = p
-12(%rbp) = c

となっています。(間違っていたらご指摘ください。)

  call    __ctype_b_loc@PLT
    movq    (%rax), %rax
    movl    -12(%rbp), %edx
    movslq    %edx, %rdx
    addq    %rdx, %rdx
    addq    %rdx, %rax
    movzwl    (%rax), %eax
    movzwl    %ax, %eax
    andl    $512, %eax
    testl    %eax, %eax


読めない箇所はここです。

一体何をやっているんでしょうか?
足し算なんてCのプログラム内にはインクリメント以外出てきていません。
なぜ、こんなにadd命令が多いのでしょう?(512なんて知りません。)

__ctype_b_loc@PLTのパラメータはその直前のeaxレジスタ経由でしょうか?
rdiはインクリメントによって変化していないので、eax以外は考えられませんが・・・
PLT経由の関数の呼び出しはシステムコールの時とは違ってパラメータ用のレジスタは決まっていないということですかね・・・・
この関数はislower()に相当するかと思います。

movq    (%rax), %rax
わざわざカッコを付けている理由はなんでしょうか?
() ・・・括弧内のレジスタにアドレスが入っていたら、()はC言語で言う所の間接演算子(*)と同じ効果という理解で正しいでしょうか?
この行はなぜ必要なんですか?

testl    %eax, %eax
同じレジスタのANDを取ってどうするんでしょうか??
意味がよく分かりません。

どなたか教えてください。
Linux 64bit Debianです。

[追記]
私の本のなかでは、islower()の呼び出しは以下のようになっています。
movl -12(%rbp), %eax
movl %eax, %ecx
movq __imp_islower(%rip), %rax
call *(%rax)

call命令の際のレジスタについているアスタリスクはなんのために付いているのでしょうか?
こちらの方が分かりやすいですね。

[追記2]
最適化-O2でやったコードを載せます。

    .file    "pointer_arg.c"
    .text
    .p2align 4,,15
    .globl    test
    .type    test, @function
test:
.LFB15:
    .cfi_startproc
    pushq    %r13
    .cfi_def_cfa_offset 16
    .cfi_offset 13, -16
    pushq    %r12
    .cfi_def_cfa_offset 24
    .cfi_offset 12, -24
    movq    %rdi, %r13
    pushq    %rbp
    .cfi_def_cfa_offset 32
    .cfi_offset 6, -32
    pushq    %rbx
    .cfi_def_cfa_offset 40
    .cfi_offset 3, -40
    subq    $8, %rsp
    .cfi_def_cfa_offset 48
    movsbq    (%rdi), %rbx
    testb    %bl, %bl
    je    .L10
    call    __ctype_b_loc@PLT
    movq    %r13, %rbp
    movq    %rax, %r12
    .p2align 4,,10
    .p2align 3
.L4:
    movq    (%r12), %rax
    testb    $2, 1(%rax,%rbx,2)
    je    .L3
    call    __ctype_toupper_loc@PLT
    movq    (%rax), %rax
    movl    (%rax,%rbx,4), %eax
    movb    %al, 0(%rbp)
.L3:
    addq    $1, %rbp
    movsbq    0(%rbp), %rbx
    testb    %bl, %bl
    jne    .L4
.L10:
    addq    $8, %rsp
    .cfi_def_cfa_offset 40
    movq    %r13, %rax
    popq    %rbx
    .cfi_def_cfa_offset 32
    popq    %rbp
    .cfi_def_cfa_offset 24
    popq    %r12
    .cfi_def_cfa_offset 16
    popq    %r13
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE15:
    .size    test, .-test
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl    main
    .type    main, @function
main:
.LFB16:
    .cfi_startproc
    subq    $40, %rsp
    .cfi_def_cfa_offset 48
    movabsq    $7812735413947559801, %rax
    movq    %rax, (%rsp)
    movabsq    $7308533390257515808, %rax
    movq    %rsp, %rdi
    movq    %rax, 8(%rsp)
    movabsq    $2948273595836448, %rax
    movq    %rax, 16(%rsp)
    call    test
    movq    %rax, %rdi
    call    puts@PLT
    xorl    %eax, %eax
    addq    $40, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE16:
    .size    main, .-main
    .ident    "GCC: (Debian 6.3.0-18) 6.3.0 20170516"
    .section    .note.GNU-stack,"",@progbits
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

0

アセンブルしたものを書きます。

それ、まだアセンブルしていないから笑。コンパイル(してアセンブリコードを生成)しただけです。
俺にもわからない事だらけのアセンブリコードだけど、大まかな想像はつきます。

この関数はislower()に相当するかと

そう、この問題は islower() だ。では islower() って、何だ?

足し算なんてCのプログラム内にはインクリメント以外出てきていません。
なぜ、こんなにadd命令が多いのでしょう?(512なんて知りません。)

君が足し算を書いていないのに、って思い上がるな(笑)。islower()を君が書いたわけではあるまい。でも、でも調べる手がかりはあるでしょ。512の根拠は簡単にわかったぞ。

movq    (%rax), %rax
括弧内のレジスタにアドレスが入っていたら、()はC言語で言う所の間接演算子(*)と同じ効果?

言うまでもないほど当たり前の事を言うが、レジスタにも、メモリにも、常に何か値が入っている。値が入っていない事なんて、ない。
「レジスタにアドレスが入っていたら」ではなく、「レジスタ%raxの値を、メモリアドレスとして、そのメモリの値を%raxに読み込む」、それだけ。rax = *rax; みたいな事です。

同じレジスタのANDを取ってどうするんでしょうか??

testl命令だけを見たら分からないかもしれないが、

   andl    $512, %eax
   testl    %eax, %eax
   je    .L3

je命令は条件分岐命令です。どんな条件で分岐するか、調べよ。testl命令の意味も、そこにある。このプロセッサのアセンブリ言語で、この3命令は決まり文句の一つと言っていい。

私の本のなかでは、islower()の呼び出しは以下のようになっています

その本は、ほぼ関数呼出でislower()を実現してるようです。それに対して今見てるコードは、islower()をインライン展開で実現してるようです。コンパイラが、どんなコードを生成するかは、コンパイラによっても、コンパイル条件(最適化オプション等々)によっても、違いが生じます。当たり前。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/11 14:30

    andl $512, %eax

    testl %eax, %eax

    論理積を取るものがお互い異なるので、仮にandlでフラグが変化しても、testl命令の際の論理積で、フラグが同じ値に変化するとは限らないような気がするんですが・・・・

    andl命令でフラグを変化できないための補うtestl命令なら、testl $512, %eaxとしなくてはいけないのではないでしょうか?

    キャンセル

  • 2017/07/11 14:43 編集

    > え!andはZF変化しますよね?

    え?そうでしたっけ?この辺りが68Kなんかと違ったと記憶してましたが???
    だとしたら、gccのコード生成、ダサすぎじゃあ。。。

    キャンセル

  • 2017/07/11 14:44

    68kと違うのはmovでフラグが変化するかどうか、ってやつじゃないですかねw

    キャンセル

0

間違いを教えながらベストアンサーにしてもらったままでは申し訳ないので、少しサービス(笑)。

call *(%rax) 
>>%raxに関数のポインタが入ってる場合、*を付けるという理解で正しいですよね。
違う。
    絶対アドレス(absolute)の場合は * を付ける。
    相対アドレス(PC relative)の場合は付けない。

違うんですか・・・・同じことだと思ったのですが・・・・

命令が同じでも、オペランドの形式が異なると操作対象が異なる、即ち機械語の動作が異なる。いわゆるアドレッシングモードの件。
上のやり取りを見て、call命令のアドレッシングモードがややこしく感じたので、思いつく限りの呼出方を並べて、as はどう扱うのか試してみました。
subr: は、呼ばれると %rax を +1 して返す。これを、aFunc: は何度も呼び出す(acode.s)。

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

        call    subr           # 最も普通
        call    (subr)         # 上と同じコード

        call    *(xx)          # xx経由
        call    *xx            # xx経由

        mov     $subr, %rdx    # 絶対アドレスをロード
        call    *%rdx          # 
        call    %rdx           # Warning
        lea     subr(%rip), %rdx # 実効アドレスをロード, PIE
        call    *%rdx          # 
        call    %rdx           # Warning

        lea     xx(%rip), %rdx # アドレスが置かれたメモリのアドレスをロード
        call    *(%rdx)        # xx経由
        call    (%rdx)         # Warning
        ret


呼出側(ccode.c)。

#include <stdio.h>
int aFunc(void);
int main(void)
{
    printf("subr() has been called %d times.\n", aFunc());
    return 0;
}


コンパイルと動作確認はシンプルに。

$ cc ccode.c acode.s 
acode.s: Assembler messages:
acode.s:18: Warning: indirect call without `*'
acode.s:22: Warning: indirect call without `*'
acode.s:26: Warning: indirect call without `*'
$ ./a.out
subr() has been called 10 times.


アセンブラが警告warningを出す命令は、直前の命令と同じ機械語が生成されることが確認できるはず。「お前、手を抜いて'*'を省いたな。今回は'*'を補ってアセンブルしてやるから、次はちゃんと書けよ(笑)」。ちなみに、手元のasはバージョン2.26.1。

さて、callに限らず、分岐命令とは、CPU内部で命令ポインタ%ripの値を変更する命令です。
何の値が%ripに代入されるかという違いがcall命令のアドレッシングモードの違いです。試した命令は次の4種類に整理できそうです。

1.「call ラベル」「call (ラベル)」
例:"call subr", "call (subr)"
前者は最も一般的な形式。機械語に含まれる(機械語にエンコードされる、とも言う)のはラベルまでの距離。いわゆるPC相対分岐です。
%rip = %rip + ラベルまでの距離

2.「call *ラベル」「call *(ラベル)」
例:"call *xx", "call *(xx)"
%rip = ???

3.「call *%レジスタ」「call %レジスタ」
例:"call *%rdx", "call %rdx"
%rip = ???

4.「call *(%レジスタ)」「call (%レジスタ)」
例:"call *(%rdx)", "call (%rdx)"
%rip = ???

%raxに関数のポインタが入ってる場合、*を付けるという理解で正しいですよね

というような、字面・上辺だけで早合点しようとするのではなく、(少なくとも一度は)具体的に・深く・何か工夫して・予想を立てられるなら、それに基づいて・自分の手を動かして・確かめて欲しいものです。ということで、こんな確かめ方もあるよという紹介をしたので、2.〜4.は???の部分をstrike君への穴埋め問題とします笑。技術的に通じる日本語で回答するように。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/14 12:10

    あら・・・・
    別のLinux mintを使ってやってみました。
    できました!!

    それで・・・私の回答の答え合わせをお願いします。

    キャンセル

  • 2017/07/14 12:12

    こちらのlinuxはgccのバージョンが古いですね。
    古い方だとできるのかな?

    キャンセル

  • 2017/07/14 14:04

    これについては再度質問します。

    キャンセル

-2

コメントが長くなってしまったので、こちらに書きますね。

#define islower(c) ((*__ctype_b_loc())[(int) (c)] & (unsigned short int )_ISlower)

andl $512, %eax
rax = 0x200(512)
の後・・・

testl    %eax, %eax
これがまだ謎ですね・・・・同じものをAND取ってどうするのか分からないですね・・・・

C = C+C;
rax = rax + C;
あとこれの必要性も見えないです。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/11 18:17 編集

    call *(%rax)
    %raxに関数のポインタが入ってる場合、*を付けるという理解で正しいですよね。

    http://d.hatena.ne.jp/pakepion/20081021/1224592877

    ふぅ、解決いたしましたわ。
    fuzzballさんに評価を入れられないので、rubato6809さんをBestAnswerにいたしますわ。

    キャンセル

  • 2017/07/11 18:21 編集

    >>%raxに関数のポインタが入ってる場合、*を付けるという理解で正しいですよね。
    違う。
    絶対アドレス(absolute)の場合は * を付ける。相対アドレス(PC relative)の場合は付けない。

    スルー耐性の高さだけは褒めてあげるよw

    キャンセル

  • 2017/07/11 18:23

    む!

    違うんですか・・・・同じことだと思ったのですが・・・・

    キャンセル

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

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

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