ftraceについて調べる(前々回からの続き)
前々回では「スタック弄りはなさそう」と書いた。
ところで、今回参照した論文でスタック弄りについて触れた部分のタイトルが「2 Adding Function Graph Tracing to ARM」となっていた。
このスタック弄りの概要を図示すると、以下のとおりとなる。
確かに前々回まででは「Graph Trace」の部分は読めていなかった。もう一度mcountを見直すと以下のように関数ポインタの値を判断したうえでftrace_graph_callerを呼び出している。
PTR_LA t1, ftrace_stub (中略) #ifdef CONFIG_FUNCTION_GRAPH_TRACER PTR_L t3, ftrace_graph_return bne t1, t3, ftrace_graph_caller nop PTR_LA t1, ftrace_graph_entry_stub PTR_L t3, ftrace_graph_entry bne t1, t3, ftrace_graph_caller nop #endif ||< ftrace_graph_callerは以下の関数。 >|C| #ifdef CONFIG_FUNCTION_GRAPH_TRACER NESTED(ftrace_graph_caller, PT_SIZE, ra) #ifdef CONFIG_DYNAMIC_FTRACE PTR_L a1, PT_R31(sp) /* load the original ra from the stack */ #ifdef KBUILD_MCOUNT_RA_ADDRESS PTR_L t0, PT_R12(sp) /* load the original t0 from the stack */ #endif #else MCOUNT_SAVE_REGS move a1, ra /* arg2: next ip, selfaddr */ #endif #ifdef KBUILD_MCOUNT_RA_ADDRESS bnez t0, 1f /* non-leaf func: t0 saved the location of the return address */ nop PTR_LA t0, PT_R1(sp) /* leaf func: get the location of at(old ra) from our own stack */ 1: move a0, t0 /* arg1: the location of the return address */ #else PTR_LA a0, PT_R1(sp) /* arg1: &AT -> a0 */ #endif jal prepare_ftrace_return #ifdef CONFIG_FRAME_POINTER move a2, fp /* arg3: frame pointer */ #else #ifdef CONFIG_64BIT PTR_LA a2, PT_SIZE(sp) #else PTR_LA a2, (PT_SIZE+8)(sp) #endif #endif MCOUNT_RESTORE_REGS RETURN_BACK END(ftrace_graph_caller)
この関数ではprepare_ftrace_returnがコールされる。
/* * Hook the return address and push it in the stack of return addrs * in current thread info. */ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, unsigned long fp) { unsigned long old; struct ftrace_graph_ent trace; unsigned long return_hooker = (unsigned long) &return_to_handler; int faulted; if (unlikely(atomic_read(¤t->tracing_graph_pause))) return; /* "parent" is the stack address saved the return address of the caller * of _mcount. * * if the gcc < 4.5, a leaf function does not save the return address * in the stack address, so, we "emulate" one in _mcount's stack space, * and hijack it directly, but for a non-leaf function, it save the * return address to the its own stack space, we can not hijack it * directly, but need to find the real stack address, * ftrace_get_parent_addr() does it! * * if gcc>= 4.5, with the new -mmcount-ra-address option, for a * non-leaf function, the location of the return address will be saved * to $12 for us, and for a leaf function, only put a zero into $12. we * do it in ftrace_graph_caller of mcount.S. */ /* old = *parent; */ safe_load_stack(old, parent, faulted); if (unlikely(faulted)) goto out; #ifndef KBUILD_MCOUNT_RA_ADDRESS parent = (unsigned long *)ftrace_get_parent_addr(self_addr, old, (unsigned long)parent, fp); /* If fails when getting the stack address of the non-leaf function's * ra, stop function graph tracer and return */ if (parent == 0) goto out; #endif /* *parent = return_hooker; */ safe_store_stack(return_hooker, parent, faulted); if (unlikely(faulted)) goto out; if (ftrace_push_return_trace(old, self_addr, &trace.depth, fp) == -EBUSY) { *parent = old; return; } trace.func = self_addr; /* Only trace if the calling function expects to */ if (!ftrace_graph_entry(&trace)) { current->curr_ret_stack--; *parent = old; } return; out: ftrace_graph_stop(); WARN_ON(1); }
ソースのコメントから、KBUILD_MCOUNT_RA_ADDRESSはgcc4.5以降で有効なオプションと推定できる。
長くなるので、続きは次回。次回以降はスタック弄りを見たいので、gcc4.5未満のgccを使っていると仮定してソースを読む。