これが私の答えの核の核です:いくつかのコードを書いてください。
私の答えの核心は次のとおりです。コンパイラが常にローカルをスタックに割り当てる場合、...
関数の名前を記録するすべての関数エントリでスタックにblobを追加し、いくつかのマジックナンバーを投入して、スタックのスマッシュをキャッチします。
typedef struct stack_debug_blob_ {
int magic1;
const char * function_name;
int magic2;
struct stack_debug_blob_ * called_by;
int magic3;
} stack_debug_blob;
stack_debug_blob * top_of_stack_debug_blobs = 0;
関数の名前をとってマクロENTER(f)を作成します。マクロは、{を開いた後のすべての関数のコードの最初の行にある必要があります。(const)char *関数名へのポインター、スタック上の前の構造体へのポインター、および正気度をチェックするためのいくつかのマジックナンバーを含む構造体を追加します。この新しい構造体でblobスタックポインタの上部をポイントします。
#define ENTER(f) \
stack_debug_blob new_stack_debug_blob = { \
MAGIC1, (f), MAGIC2, top_of_stack_debug_blobs, MAGIC3}; \
stack_debug_blob * evil_hack = (top_of_stack_debug_blobs = (&new_stack_debug_blob))
可能な限り移植性を保つために、ENTERでできることは、変数を宣言して初期化することだけです。したがって、evil_hackは、変数を初期化するだけでなく、少し余分な計算を行います。
ポインタとマジックナンバーをチェックするblobのリストをたどる関数を作成します。混乱していることがわかった場合は、エラーを通知する必要があります(stderrに出力する、while(1){/ * nada * /}でCPUをロックする、デバッガーに入る...ハードウェアによって異なります)。
ブロブのスタックをチェックするマクロEXIT()を作成し、リンクリストから最上位のリンクを解除します。すべての関数の出口に配置する必要があります。
#define EXIT() do { \
check_debug_blobs(); \
top_of_stack_debug_blobs = new_stack_debug_blob.called_by; \
new_stack_debug_blob.magic1 -= 1; /* paranoia */ \
} while (0)
おそらく、すべてのreturnをRETURNマクロ呼び出しに置き換える必要もあります。RETURNマクロはEXITと同じですが、} while(0)の前にreturnがあります。
関数名を出力するblobのリストをたどる関数を作成し、おそらくスタックトレースやバックトレースのような名前を付けます。
ENTER(f)、EXIT()、RETURN(x)を呼び出して、Cコードをインストルメント化するプログラムを作成します。
あなたがそれを楽しむことができるようにいくつかの詳細を省略しました...
uclibcのバックトレースで利用可能な移植も参照してください。