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(&current->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を使っていると仮定してソースを読む。