基本的に glibc の backtrace() ライブラリ関数と同じように、例外ハンドラとデバッグ関数でコール スタック バックトレースを出力できるようにしたいと考えています。残念ながら、私の C ライブラリ (Newlib) はそのような呼び出しを提供していません。
私はこのようなものを持っています:
#include <unwind.h> // GCC's internal unwinder, part of libgcc
_Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *d)
{
int *depth = (int*)d;
printf("\t#%d: program counter at %08x\n", *depth, _Unwind_GetIP(ctx));
(*depth)++;
return _URC_NO_REASON;
}
void print_backtrace_here()
{
int depth = 0;
_Unwind_Backtrace(&trace_fcn, &depth);
}
これは基本的に機能しますが、結果として得られるトレースは必ずしも完全ではありません。たとえば、
int func3() { print_backtrace_here(); return 0; }
int func2() { return func3(); }
int func1() { return func2(); }
int main() { return func1(); }
バックトレースには func3() と main() のみが表示されます。(これは明らかなおもちゃの例ですが、逆アセンブルを確認し、これらの関数がすべて完全にここにあり、最適化またはインライン化されていないことを確認しました。)
更新:古い ARM7 システムでこのバックトレース コードを試しましたが、同じ (または少なくとも、可能な限り同等の) コンパイラ オプションとリンカー スクリプトを使用して、正しい完全なバックトレースを出力しました (つまり、func1 と func2 が欠落していません)。実際、メインを過ぎてブート初期化コードにまで遡ることさえできます。したがって、おそらく問題はリンカー スクリプトやコンパイラ オプションにあるわけではありません。(また、このARM7テストでもフレームポインタが使用されていないことを逆アセンブルから確認しました)。
コードは -fomit-frame-pointer でコンパイルされていますが、私のプラットフォーム (ベア メタル ARM Cortex M3) はフレーム ポインターを使用しない ABI を定義しています。(このシステムの以前のバージョンでは、ARM7 で古い APCS ABI を使用し、スタック フレームとフレーム ポインターを強制し、こちらのようなバックトレースを使用していましたが、完全に機能していました)。
システム全体が -fexception でコンパイルされ、_Unwind が使用する必要なメタデータが ELF ファイルに含まれるようになります。(_Unwind は例外処理用に設計されていると思います)。
だから、私の質問は次のとおり です。GCCを使用して組み込みシステムで信頼できるバックトレースを取得する「標準」の受け入れられた方法はありますか?
必要に応じてリンカー スクリプトと crt0 コードをいじる必要はありませんが、ツールチェーン自体にチャンスを与える必要はありません。
ありがとう!