MIRACLE
メールサービス申込 ユーザー登録 パートナー情報
お問い合わせ FAQ サイトマップ
MIRACLE LINUXの特長 製品紹介 サービス案内 購入 サポート 技術フォーラム

プロフィール

日本発のリナックス企業、ミラクル・リナックスで奮闘する社員のブログです。

ミラクル関連リンク

採用情報

サイト検索

最近のトラックバック

2008年8月

          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

« 超整理法 | メイン | 勉強会 »

アセンブラの勉強方法 2

前回(アセンブラの勉強方法)のテストプログラムではmov、cmp、jmp、add命令など見ることができましたが、少し足りないのでもう少し見てみます。

今回のテストプログラムでは関数コールをしてみます。

あとはわかりづらいインラインアセンブラも入れてみます。逆アセンブラすればインラインアセンブラもなんとなくわかります。

前回と同じようにテストプログラムを作成して、コンパイルします。( Fedora Core 5 32bit の場合 )

# cat assemble2.c
#include <stdio.h>

/*
* include/asm-i386/atomic.h
*/
#ifdef CONFIG_SMP
#define LOCK "lock ; "                  ・・・・(4)
#else
#define LOCK ""
#endif
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i)  { (i) }

static __inline__ void atomic_inc(atomic_t *v) ・・・・(7)
{
        __asm__ __volatile__(
                LOCK "incl %0"          ・・・・(3) assembler template
                :"=m" (v->counter)      ・・・・(5) output operand
                :"m" (v->counter));     ・・・・(6) input operand
}

int func(atomic_t *cnt)
{
        atomic_inc(cnt);                ・・・・(3)
        return 0;
}

int main(void)
{
        atomic_t cnt=ATOMIC_INIT(0);    ・・・・(1)
        func(&cnt);                     ・・・・(2)
        return 0;
}

インラインアセンブラの例としてLinuxカーネル(2.6.15)のinclude/asm-i386/atomic.h からatomic_inc()をそのままコピーしています。コンパイルは-DオプションでCONFIG_SMPを有効にします。

# gcc -Wall -O0 -DCONFIG_SMP assemble2.c -o assemble2
# ls
assemble2.c assemble2
# objdump -d assemble2
・・・・
08048354 <func>:
8048354:   55                   push   %ebp
8048355:   89 e5                mov    %esp,%ebp
8048357:   83 ec 08             sub    $0x8,%esp
804835a:   8b 45 08             mov    0x8(%ebp),%eax
804835d:   89 04 24             mov    %eax,(%esp)
8048360:   e8 07 00 00 00       call   804836c <atomic_inc>  ・・(7)
8048365:   b8 00 00 00 00       mov    $0x0,%eax
804836a:   c9                   leave 
804836b:   c3                   ret   

0804836c <atomic_inc>:
804836c:   55                   push   %ebp
804836d:   89 e5                mov    %esp,%ebp
804836f:   8b 55 08             mov    0x8(%ebp),%edx        ・・(5)
8048372:   8b 45 08             mov    0x8(%ebp),%eax        ・・(6)
8048375:   f0 ff 02             lock incl (%edx)             ・・(3)(4)
8048378:   5d                   pop    %ebp
8048379:   c3                   ret   

0804837a <main>:
804837a:   8d 4c 24 04          lea    0x4(%esp),%ecx
804837e:   83 e4 f0             and    $0xfffffff0,%esp
8048381:   ff 71 fc             pushl  0xfffffffc(%ecx)
8048384:   55                   push   %ebp
8048385:   89 e5                mov    %esp,%ebp
8048387:   51                   push   %ecx
8048388:   83 ec 14             sub    $0x14,%esp
804838b:   c7 45 f8 00 00 00 00 movl   $0x0,0xfffffff8(%ebp) ・・(1)
8048392:   8d 45 f8             lea    0xfffffff8(%ebp),%eax
8048395:   89 04 24             mov    %eax,(%esp)
8048398:   e8 b7 ff ff ff       call   8048354 <func>        ・・(2)
804839d:   b8 00 00 00 00       mov    $0x0,%eax             ・・[1]
80483a2:   83 c4 14             add    $0x14,%esp
80483a5:   59                   pop    %ecx
80483a6:   5d                   pop    %ebp
80483a7:   8d 61 fc             lea    0xfffffffc(%ecx),%esp
80483aa:   c3                   ret   
80483ab:   90                   nop   
・・・・

(1)でatomic変数を初期化しています。
(2)でfunc()を呼び出し(call)しています。call命令とjmp命令の違いですが、jmpはジャンプして終わりですが、callはまた戻ってきます。call命令の場合はスタックポインタespに戻るアドレスを保存します。
この場合[1]の"804839d"がespに残ります。(注1)

(3)はインラインアセンブラの最初の命令です。gccオプションに"-D"オプションを指定したので(4)のlockプレフィックスが付いてアトミック操作になります。

(5)、(6)がわからないところですが、(5)はoutput operandだそうです。"="はoutputを意味します。
"m"はmemory operandだそうです。アトミック変数をローカルでスタックに定義しているので当然ですがメモリです。
(6)はeaxレジスタに一時的に読み出しています。意味がなさそうです。というのも(3)で%0しか使っていません。(注2)
%0はoperand numberで最初(0番目)のオペランドの意味です。今回でいうと0番目は(5)です。(6)は%1ですが%1は使っていません。
(3)はgccマニュアルでassembler templateとありオペランドではないみたいです。

gccマニュアルはhttp://gcc.gnu.org/onlinedocs/からダウンロードできます。
今回は以下を参考にしました。

5.34 Assembler Instructions with C Expression Operands
  The `=' in `=f' indicates that the operand is an output

5.35.1 Simple Constraints
`m'
  A memory operand is allowed, with any kind of address that the machine
  supports in general.

`0', `1', `2', ... `9'
  An operand that matches the specified operand number is allowed.

(7)ではatomic_inc()が__inline_で宣言されているのでのですが、call命令で関数コールされています。これはコンパイルのときに最適化オプションをオフ(-O0)にしているのが原因です。-O2オプションでコンパイルすると以下のようにインラインになります(func()関数の中にatomic_inc()が組み込まれます)。

# gcc -Wall -O2 -DCONFIG_SMP assemble2.c -o assemble2
# objdump -d assemble2
・・・・
08048360 <func>:
8048360:   55                   push   %ebp
8048361:   89 e5                mov    %esp,%ebp
8048363:   8b 45 08             mov    0x8(%ebp),%eax
8048366:   f0 ff 00             lock incl (%eax)
8048369:   5d                   pop    %ebp
804836a:   31 c0                xor    %eax,%eax
804836c:   c3                   ret   
804836d:   8d 76 00             lea    0x0(%esi),%esi
・・・・

(注1):ダンプのバックトレースなどでは[1]のようにcall命令の次のアドレスが表示されます。
            実際には1行上のcall命令を実行したことになります。

(注2):ちなみに2.6.19ではコードが変更されていおり、無駄のないコードとなっていました。
            以下のatomic_inc()のコードに変更して-O0でコンパイルすると(6)が消えました(下記参照)。
            (カーネルのコンパイルは-O2で最適化されるので特に影響はありません)

static __inline__ void atomic_inc(atomic_t *v)
{
        __asm__ __volatile__(
                LOCK_PREFIX "incl %0"
                :"+m" (v->counter));
}
・・・・
0804836c <atomic_inc>:
804836c:   55                    push   %ebp
804836d:   89 e5                 mov    %esp,%ebp
804836f:   8b 45 08              mov    0x8(%ebp),%eax
8048372:   f0 ff 00              lock incl (%eax)
8048375:   5d                    pop    %ebp
8048376:   c3                    ret   
・・・・

トラックバック

このページのトラックバックURL:
http://www.typepad.jp/t/trackback/4447/6950975

このページへのトラックバック一覧 アセンブラの勉強方法 2:

コメント

コメントを投稿

会社情報 採用情報 個人情報保護方針 商標等取り扱い事項 English
Copyright(c)2000-2006 MIRACLE LINUX CORPORATION. All Rights Reserved.