On 4/15/21 11:18 AM, Steven Rostedt wrote:
On Thu, 15 Apr 2021 19:39:45 +0200
Jiri Olsa <jo...@redhat.com> wrote:

I don't know how the BPF code does it, but if you are tracing the exit
of a function, I'm assuming that you hijack the return pointer and replace
it with a call to a trampoline that has access to the arguments. To do

hi,
it's bit different, the trampoline makes use of the fact that the
call to trampoline is at the very begining of the function and, so
it can call the origin function with 'call function + 5' instr.

so in nutshell the trampoline does:

   call entry_progs
   call original_func+5

How does the above handle functions that have parameters on the stack?

In arch/x86/net/bpf_jit_comp.c, the following code

int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *image_end,
                                const struct btf_func_model *m, u32 flags,
                                struct bpf_tramp_progs *tprogs,
                                void *orig_call)
{
        int ret, i, cnt = 0, nr_args = m->nr_args;
        int stack_size = nr_args * 8;
        struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY];
        struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT];
struct bpf_tramp_progs *fmod_ret = &tprogs[BPF_TRAMP_MODIFY_RETURN];
        u8 **branches = NULL;
        u8 *prog;

/* x86-64 supports up to 6 arguments. 7+ can be added in the future */
        if (nr_args > 6)
                return -ENOTSUPP;
...

Here, nr_args will be maximumly 5 original arguments + "ret" value argument for fexit bpf program.

yes, it is a known limitation. I guess more can be added and pushed to
the stack. Looks like there is no strong request so this has not been
implemented yet.


   call exit_progs

you can check this in arch/x86/net/bpf_jit_comp.c in moe detail:

  * The assembly code when eth_type_trans is called from trampoline:
  *
  * push rbp
  * mov rbp, rsp
  * sub rsp, 24                     // space for skb, dev, return value
  * push rbx                        // temp regs to pass start time
  * mov qword ptr [rbp - 24], rdi   // save skb pointer to stack
  * mov qword ptr [rbp - 16], rsi   // save dev pointer to stack
  * call __bpf_prog_enter           // rcu_read_lock and preempt_disable
  * mov rbx, rax                    // remember start time if bpf stats are 
enabled
  * lea rdi, [rbp - 24]             // R1==ctx of bpf prog
  * call addr_of_jited_FENTRY_prog  // bpf prog can access skb and dev

entry program called ^^^

  * movabsq rdi, 64bit_addr_of_struct_bpf_prog  // unused if bpf stats are off
  * mov rsi, rbx                    // prog start time
  * call __bpf_prog_exit            // rcu_read_unlock, preempt_enable and 
stats math
  * mov rdi, qword ptr [rbp - 24]   // restore skb pointer from stack
  * mov rsi, qword ptr [rbp - 16]   // restore dev pointer from stack
  * call eth_type_trans+5           // execute body of eth_type_trans

original function called ^^^

This would need to be limited to only functions that do not have any
parameters on the stack.


  * mov qword ptr [rbp - 8], rax    // save return value
  * call __bpf_prog_enter           // rcu_read_lock and preempt_disable
  * mov rbx, rax                    // remember start time in bpf stats are 
enabled
  * lea rdi, [rbp - 24]             // R1==ctx of bpf prog
  * call addr_of_jited_FEXIT_prog   // bpf prog can access skb, dev, return 
value

exit program called ^^^

  * movabsq rdi, 64bit_addr_of_struct_bpf_prog  // unused if bpf stats are off
  * mov rsi, rbx                    // prog start time
  * call __bpf_prog_exit            // rcu_read_unlock, preempt_enable and 
stats math
  * mov rax, qword ptr [rbp - 8]    // restore eth_type_trans's return value
  * pop rbx
  * leave
  * add rsp, 8                      // skip eth_type_trans's frame
  * ret                             // return to its caller

this you need a shadow stack to save the real return as well as the
parameters of the function. This is something that I have patches that do
similar things with function graph.

If you want this feature, lets work together and make this work for both
BPF and ftrace.

it's been some time I saw a graph tracer, is there a way to make it
access input arguments and make it available through ftrace_ops
interface?

I have patches that could easily make it do so. And should probably get
them out again. The function graph tracer has a shadow stack, and my
patches allow you to store data on it for use with the exiting of the
program.

My last release of that code is here:

   https://lore.kernel.org/lkml/20190525031633.811342...@goodmis.org/

It allows you to "reserve data" to pass from the caller to the return, and
that could hold the arguments. See patch 15 of that series.


-- Steve

Reply via email to