ここで言う fp とは浮動小数点演算のことです.
かつては x86 も,FPU (Floating Poing Unit; 浮動小数点演算ユニット)は 別付けのオプションでした. SoC ではその用途上,浮動小数点演算を駆使する機会はあまりありません. このため,現在でも浮動小数点演算ユニットを搭載していない石が多く存在し ます.
が,「たまには」浮動小数点演算が必要となる機会もあります. この場合,浮動小数点演算を処理させるにはいくつかの方法があります.
浮動小数点演算ユニットの無い CPU で浮動小数点演算命令を実行しようとす ると,未定義命令等の例外が発生します. この例外を捕捉し,例外ハンドラ中で浮動小数点演算命令をエミュレートし, 例外発生元に復帰します.
この場合の長所は,浮動小数点演算ユニット用の実行プログラムがそのまま実 行できることです. ただし,浮動小数点演算ユニットがある場合に比べ,実行速度は格段に落ちま す.
短所は,「オーバヘッドが大きい」ことです. 浮動小数点演算命令ごとに例外が発生し,コンテキストスイッチが発生します. そして,例外ハンドラ中でエミュレーションを行い,元のコンテキストに復帰 します.
ここでは,プログラムのコンパイル時に浮動小数点演算ライブラリとリンクす
るという方法を検討します.
このライブラリ中で浮動小数点演算を整数演算で処理しよう,というものです.
gcc では --msoft-float オプションで使用することができます.
この場合は実行プログラムのレベルで浮動小数点演算ライブラリを呼び出しま す. このため,浮動小数点演算を行っても例外の発生は無く,コンテキストスイッ チも発生しません.
が,この実行プログラムを FPU がある環境に持っていっても,実行速度は上 がりません. せっかく持ってる FPU を使わずに演算ライブラリの整数演算で浮動小数点演 算を行うためです.
これまで出てきた3つの浮動小数点演算処理の実行速度は
(遅) FPU エミュレーション < 浮動小数点演算ライブラリ < 実 FPU での演算 (速)となります.
と,ここまでは一般的な浮動小数点演算の話でした. ARM の場合は更に一癖あります.
説明の前に現象から入ってみましょう.
以下の2つの C のソースプログラムを用意します.
/* foo.c */
int foo ( int a, int b )
{
return a + b;
}
/* main.c */
#include <stdio.h>
extern int foo ( int a, int b );
main ( void )
{
printf ( "%d\n", foo ( 1, 2 ) );
}
関数を別ソースファイルに記述した,何の変哲もない2本の C プログラムで す.
これらを以下の手順でコンパイルしてみます.
$ gcc foo.c -msoft-float -c
$ gcc main.c -c
$ gcc foo.o main.o
foo.c のほうは -msoft-float オプション付きで,main.c の
ほうはオプション無しでコンパイルし,オブジェクトファイルをリンクします.
これを x86 や PowerPC 上で実行すると,何の問題もなく a.out 実行ファ
イルが作成されます.
ところが,ARM toolchain 上ではリンクの段階で
$ gcc main.o foo.o
/usr/bin/ld: ERROR: /usr/lib/gcc/arm-linux-gnu/4.0.4/libgcc_s.so uses hardware
FP, whereas a.out uses software FP
/usr/bin/ld: failed to merge target specific data of file /usr/lib/gcc/arm-linu
x-gnu/4.0.4/libgcc_s.so
/usr/bin/ld: ERROR: /lib/libc.so.6 uses hardware FP, whereas a.out uses softwar
e FP
(略)
collect2: ld returned 1 exit status
というエラーでリンクに失敗します.
大量のエラーメッセージが出力されますが,ほとんどは以下のようなエラーで す.
/usr/bin/ld: ERROR: /usr/lib/gcc/arm-linux-gnu/4.0.4/libgcc_s.so uses hardware
FP, whereas a.out uses software FP
ということは,このエラーがポイントのようです.
メッセージを素直に解釈すると「一方は hardware FP で他方は software FP である」と読めます. が,このエラーメッセージで google 検索をすると,「それは toolchain のバグ だ.最新版にアップグレードせよ」など,意味不明のアドバイスが得られます. しかし,それがどういうバグなのか言及しているページは見付からないようで す
まるで「牛の首」のような….
というわけで,目の前の現象を見てみることにします.
オブジェクトファイルの形式を調べてみましょう.
それには readelf コマンドを使用します.
以下は,x86 のオブジェクトファイルのヘッダ情報を出力した例です.
$ readelf -h foo.o
ELF ヘッダ:
マジック: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
クラス: ELF32
データ: 2 の補数、リトルエンディアン
バージョン: 1 (current)
OS/ABI: UNIX - System V
ABI バージョン: 0
タイプ: REL (再配置可能ファイル)
マシン: Intel 80386
バージョン: 0x1
エントリポイントアドレス: 0x0
プログラムの開始ヘッダ: 0 (バイト)
セクションヘッダ始点: 192 (バイト)
フラグ: 0x0
このヘッダのサイズ: 52 (バイト)
プログラムヘッダサイズ: 0 (バイト)
プログラムヘッダ数: 0
セクションヘッダ: 40 (バイト)
Number of section headers: 9
Section header string table index: 6
で,x86 オブジェクトファイルをこのコマンドで調べてみると,以下のように 違いが見付かりません.
$ readelf -h foo.o main.o
ファイル: foo.o
ELF ヘッダ:
マジック: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
…(略)…
ファイル: main.o
ELF ヘッダ:
マジック: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
…(略)…
が,ARM オブジェクトファイルでは
$ readelf -h foo.o main.o
ファイル: foo.o
ELF ヘッダ:
マジック: 7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
…(略)…
フラグ: 0x200, GNU EABI, software FP
…(略)…
ファイル: main.o
ELF ヘッダ:
マジック: 7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
…(略)…
フラグ: 0x0
…(略)…
と,2つのオブジェクトファイル間で相違が見られ,
ELF ヘッダ上に soft FP か hard FP かの情報が埋め込まれているということがわかります. リンカステージではこれを見て「種類か違うのでリンクしてはいけない」と判 断し,エラーメッセージを出力してるわけです.
そもそも,soft FP と hard FP では浮動小数点の内部表現が一致していると は限らないので,これらのオブジェクトファイルを混ぜてリンクすべきではあ りません.
readelf コマンドでオブジェクトファイルの形式を調べ,soft FP か
hard FP のいずれかに形式を統一するよう,コンパイルオプションを調整しま
しょう.
実は,リンカオプションに --no-warn-mismatch
(gcc へは--Wl,--no-warn-mismatch)と追加すると,soft FP と hard
FP のオブジェクトファイルを強引にリンクすることができます.
$ gcc -Wl,--no-warn-mismatch foo.o main.o
「プログラム内部では浮動小数点演算は全く使ってないのだけど,なぜかリン カステージで『浮動小数点がうんぬん』のエラーで跳ねられる」という場合は, この方法も有効でしょう.