* ARM Thumb 命令セット Super Interworking [#o5dc4cb7] gcc での Thumb 命令サポートの Super Interworking について. ** オプションの説明 [#z73ab484] 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 のほうは関数ポインタでの呼び出しをうまく処理してくれるようです. どう処理してくれるかはこの説明だけではよくわかりませんね. ** 環境 [#f4421372] 今回試した環境は :CPU|88F5182 (I-O DATA HDL-GXR) :カーネル|linux 2.6.12.6 :ユーザランド|debian etch です. ** Callee Super Interworking [#m513a65e] まずはこっちのほうを試してみましょう. *** ソース [#l1b8b1dc] 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; } *** コンパイル [#z8842c9f] コンパイルします. main.c は ARM 命令セットで,thumb.c は Thumb 命令セットでコンパイルします. $ gcc -mthumb -mcallee-super-interworking thumb.c -c $ gcc main.c -c *** ディスアセンブル [#k99f4094] $ 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 ヘッダ [#q0af6099] 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 *** リンク [#vd5e7b82] というわけで,リンクは何も小細工無しに通ってしまいます. $ gcc -o main main.o thumb.o *** 実行 [#tf203a7a] もちろん,問題なく実行できます. $ ./main 2 ** Caller Super Interworking [#yb859ceb] というわけで,謎の Caller Super Interworking について. *** ソース [#b8ca9611] 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 という経路で関数が呼び出されます. *** コンパイル [#zb3ef2b4] $ gcc -mthumb -mcallee-super-interworking -mcaller-super-interworking thumb.c -c $ gcc main.c -c *** ディスアセンブル [#a1a5bf7e] $ 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 ヘッダ [#y51b7705] $ 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 ファイルと一緒ですね. *** リンク [#b3b19011] というわけでリンクしてみます. $ 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 が見つからず,リンクが失敗しています. 今回の環境ではこの関数は用意されてないようです. 残念.