__aeabi_idiv0() を使用したゼロによる整数除算エラーのトラップ
ゼロによる整数除算エラーは C ライブラリヘルパ関数の __aeabi_idiv0()
を使用してトラップできるため、ゼロによる除算が発生したときにゼロなどの標準的な結果が返されるようにすることができます。
整数除算は、C ライブラリヘルパ関数 __aeabi_idiv()
および __aeabi_uidiv()
を使用してコードに実装されます。どちらの関数でもゼロ除算の有無がチェックされます。
ゼロによる整数除算が検出されると、__aeabi_idiv0()
への分岐が作成されます。そのため、__aeabi_idiv0()
上にブレークポイントを設定するだけで、ゼロ除算をトラップできます。
ライブラリには、__aeabi_idiv0()
の 2 種類の実装が用意されています。デフォルトの実行では何も行われないため、ゼロによる除算が検出されると除算関数はゼロを返します。ただし、信号処理を使用する場合は、__rt_raise(SIGFPE, DIVBYZERO)
を呼び出す別の実装が選択されます。
独自のバージョンの __aeabi_idiv0()
が指定された場合、除算関数がこの関数を呼び出します。__aeabi_idiv0()
の関数プロトタイプは次のとおりです。
int
__aeabi_idiv0(void
);
__aeabi_idiv0()
が値を返すと、その値は除算関数によって返される商として使用されます。
__aeabi_idiv0()
の呼び出し時、リンクレジスタ LR
には、アプリケーションコードの __aeabi_uidiv()
除算ルーチンの呼び出しの後に命令のアドレスが含まれています。
ソースコードで問題のある行を識別するには、LR
によって指定されたアドレスにある C コード行をデバッガで参照します。
パラメータを検証し、__aeabi_idiv0
トラップ時の事後分析デバッグ用に保存するには、$Super$$
と $Sub$$
の各メカニズムを使用します。
-
パッチを適用していない元の関数 __aeabi_idiv0()
を識別するには、__aeabi_idiv0()
に接頭辞 $Super$$
を付けます。
-
元の関数を直接呼び出すには、接頭辞 $Super$$
を付けた __aeabi_idiv0()
を使用します。
-
__aeabi_idiv0()
の元のバージョンの代わりに呼び出す新しい関数を識別するには、__aeabi_idiv0()
に接頭辞 $Sub$$
を付けます。
-
元の関数 __aeabi_idiv0()
の前または後に処理を追加するには、__aeabi_idiv0()
に接頭辞 $Sub$$
を付けます。
以下のサンプルでは、$Super$$
と $Sub$$
のメカニズムを使用して __aeabi_div0
をインターセプトする方法を示します。
extern void $Super$$__aeabi_idiv0(void);
/* この関数が元の関数 __aeabi_idiv0() の代わりに呼び出される */
void $Sub$$__aeabi_idiv0()
{
// ゼロによる除算を処理するコードを挿入する
...
// 元の __aeabi_idiv0 関数を呼び出す
$Super$$__aeabi_idiv0();
}
__rt_raise() を使用したゼロによる整数除算エラーのトラップ
ゼロによる整数除算では、デフォルトでは 0 が返されます。したがって、ゼロによる除算をインターセプトするには、シグナルを処理するための C ライブラリヘルパ関数 __rt_raise()
を再実装します。
__rt_raise()
の関数プロトタイプを以下に示します。
void
__rt_raise(int
signal, int
type);
__rt_raise()
を再実装すると、__rt_raise()
を呼び出す __aeabi_idiv0()
の信号処理ライブラリのバージョンがライブラリによって自動的に指定され、__aeabi_idiv0()
の該当のライブラリバージョンが最終イメージに含まれます。
その場合、ゼロによる除算のエラーが発生し、__aeabi_idiv0()
によって __rt_raise(SIGFPE, DIVBYZERO)
が呼び出されます。そのため、__rt_raise()
を再実装する場合は、ゼロ除算が発生したかどうかを判断する際に (signal == SIGFPE) && (type == DIVBYZERO)
をチェックする必要があります。