ARM Thumb 命令セット Super Interworking

gcc での Thumb 命令サポートの Super Interworking について.

オプションの説明

gcc の info より抜粋.

`-mcallee-super-interworking'
    Gives all externally visible functions in the file being compiled
    an ARM instruction set header which switches to Thumb mode before
    executing the rest of the function.  This allows these functions
    to be called from non-interworking code.
`-mcaller-super-interworking'
    Allows calls via function pointers (including virtual functions) to
    execute correctly regardless of whether the target code has been
    compiled for interworking or not.  There is a small overhead in
    the cost of executing a function pointer if this option is enabled.

オプションの名前は対称的なんだけど,やってることは非対称な雰囲気ですね.

-mcallee-super-interworking のほうは関数先頭で Thumb モードへの切り替えを行い,外面的には ARM 命令セットの関数として振る舞うようです. 一方,-mcaller-super-interworking のほうは関数ポインタでの呼び出しをうまく処理してくれるようです. どう処理してくれるかはこの説明だけではよくわかりませんね.

環境

今回試した環境は

CPU
88F5182 (I-O DATA HDL-GXR)
カーネル
linux 2.6.12.6
ユーザランド
debian etch

です.

Callee Super Interworking

まずはこっちのほうを試してみましょう.

ソース

main.c

#include <stdio.h>

extern int thumb_callee ( int );

int main ( void )
{
	printf ( "%d\n", thumb_callee ( 1 ) );
	return 0;
}

thumb.c

int thumb_callee ( int a )
{
	return a + 1;
}

コンパイル

コンパイルします. main.c は ARM 命令セットで,thumb.c は Thumb 命令セットでコンパイルします.

$ gcc -mthumb -mcallee-super-interworking thumb.c -c
$ gcc main.c -c

ディスアセンブル

$ objdump -d thumb.o

thumb.o:     ファイル形式 elf32-littlearm

セクション .text の逆アセンブル:

00000000 <thumb_callee>:
   0:	e38fc001 	orr	ip, pc, #1	; 0x1
   4:	e12fff1c 	bx	ip

00000008 <.real_start_ofthumb_callee>:
   8:	b580      	push	{r7, lr}
   a:	b081      	sub	sp, #4
   c:	af00      	add	r7, sp, #0
   e:	1c3b      	adds	r3, r7, #0
  10:	6018      	str	r0, [r3, #0]
  12:	1c3b      	adds	r3, r7, #0
  14:	681b      	ldr	r3, [r3, #0]
  16:	3301      	adds	r3, #1
  18:	1c18      	adds	r0, r3, #0
  1a:	46bd      	mov	sp, r7
  1c:	b001      	add	sp, #4
  1e:	bc80      	pop	{r7}
  20:	bc02      	pop	{r1}
  22:	4708      	bx	r1

関数先頭で ARM → Thumb のモード切り替えが入っています. 戻り側も bx 命令で戻っているので ARM モードに復帰するようになっています.

ELF ヘッダ

ELF ヘッダを見てみます. 両者に違いはないようです.

$ readelf -h main.o 
ELF ヘッダ:
  マジック:  7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00 
  クラス:                            ELF32
  データ:                            2 の補数、リトルエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            ARM
  ABI バージョン:                    0
  タイプ:                            REL (再配置可能ファイル)
  マシン:                            ARM
  バージョン:                        0x1
  エントリポイントアドレス:          0x0
  プログラムの開始ヘッダ:            0 (バイト)
  セクションヘッダ始点:              232 (バイト)
  フラグ:                            0x0
  このヘッダのサイズ:                52 (バイト)
  プログラムヘッダサイズ:            0 (バイト)
  プログラムヘッダ数:                0
  セクションヘッダ:                  40 (バイト)
  Number of section headers:         10
  Section header string table index: 7
$ readelf -h thumb.o 
ELF ヘッダ:
  マジック:  7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00 
  クラス:                            ELF32
  データ:                            2 の補数、リトルエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            ARM
  ABI バージョン:                    0
  タイプ:                            REL (再配置可能ファイル)
  マシン:                            ARM
  バージョン:                        0x1
  エントリポイントアドレス:          0x0
  プログラムの開始ヘッダ:            0 (バイト)
  セクションヘッダ始点:              200 (バイト)
  フラグ:                            0x0
  このヘッダのサイズ:                52 (バイト)
  プログラムヘッダサイズ:            0 (バイト)
  プログラムヘッダ数:                0
  セクションヘッダ:                  40 (バイト)
  Number of section headers:         8
  Section header string table index: 5

リンク

というわけで,リンクは何も小細工無しに通ってしまいます.

$ gcc -o main main.o thumb.o

実行

もちろん,問題なく実行できます.

$ ./main 
2

Caller Super Interworking

というわけで,謎の Caller Super Interworking について.

ソース

main.c

#include <stdio.h>

extern int thumb_callee ( int );

int main ( void )
{
	printf ( "%d\n", thumb_callee ( 1 ) );
	return 0;
}

int arm_callee ( int a )
{
	return a + 1;
}

thumb.c

extern int arm_callee ( int );
static int (*acallee)( int ) = arm_callee;

int thumb_callee ( int a )
{
	return 1 + acallee ( a );
}

main → thumb_callee → arm_callee という経路で関数が呼び出されます.

コンパイル

$ gcc -mthumb -mcallee-super-interworking -mcaller-super-interworking thumb.c -c
$ gcc main.c -c

ディスアセンブル

$ objdump -d thumb.o

thumb.o:     ファイル形式 elf32-littlearm

セクション .text の逆アセンブル:

00000000 <thumb_callee>:
   0:	e38fc001 	orr	ip, pc, #1	; 0x1
   4:	e12fff1c 	bx	ip

00000008 <.real_start_ofthumb_callee>:
   8:	b580      	push	{r7, lr}
   a:	b081      	sub	sp, #4
   c:	af00      	add	r7, sp, #0
   e:	1c3b      	adds	r3, r7, #0
  10:	6018      	str	r0, [r3, #0]
  12:	4b07      	ldr	r3, [pc, #28]	(30 <.text+0x30>)
  14:	681a      	ldr	r2, [r3, #0]
  16:	1c3b      	adds	r3, r7, #0
  18:	681b      	ldr	r3, [r3, #0]
  1a:	1c18      	adds	r0, r3, #0
  1c:	f7ff fffe 	bl	0 <_interwork_call_via_r2>
  20:	1c03      	adds	r3, r0, #0
  22:	3301      	adds	r3, #1
  24:	1c18      	adds	r0, r3, #0
  26:	46bd      	mov	sp, r7
  28:	b001      	add	sp, #4
  2a:	bc80      	pop	{r7}
  2c:	bc02      	pop	{r1}
  2e:	4708      	bx	r1
  30:	0000      	lsls	r0, r0, #0
	...

この段階では _interwork_call_via_r2 という関数経由で arm_callee を呼び出そうとしているようです.

ELF ヘッダ

$ readelf -h thumb.o
ELF ヘッダ:
  マジック:  7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00 
  クラス:                            ELF32
  データ:                            2 の補数、リトルエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            ARM
  ABI バージョン:                    0
  タイプ:                            REL (再配置可能ファイル)
  マシン:                            ARM
  バージョン:                        0x1
  エントリポイントアドレス:          0x0
  プログラムの開始ヘッダ:            0 (バイト)
  セクションヘッダ始点:              228 (バイト)
  フラグ:                            0x0
  このヘッダのサイズ:                52 (バイト)
  プログラムヘッダサイズ:            0 (バイト)
  プログラムヘッダ数:                0
  セクションヘッダ:                  40 (バイト)
  Number of section headers:         10
  Section header string table index: 7

ARM 命令セットの obj ファイルと一緒ですね.

リンク

というわけでリンクしてみます.

$ gcc -o main main.o thumb.o
thumb.o: In function `.real_start_ofthumb_callee':
thumb.c:(.text+0x1c): undefined reference to `_interwork_call_via_r2'
collect2: ld returned 1 exit status

先ほどの _interwork_call_via_r2 が見つからず,リンクが失敗しています. 今回の環境ではこの関数は用意されてないようです. 残念.


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-11-09 (月) 20:50:04 (5305d)