void *orig_call)
 {
        int ret, i, nr_args = m->nr_args;
-       int regs_off, ip_off, stack_size = nr_args * 8;
+       int regs_off, ip_off, args_off, 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];
         *                 [ ...             ]
         * RBP - regs_off  [ reg_arg1        ]  program's ctx pointer
         *
+        * RBP - args_off  [ args count      ]  always
+        *
         * RBP - ip_off    [ traced function ]  BPF_TRAMP_F_IP_ARG flag
         */
 
 
        regs_off = stack_size;
 
+       /* args count  */
+       stack_size += 8;
+       args_off = stack_size;
+
        if (flags & BPF_TRAMP_F_IP_ARG)
                stack_size += 8; /* room for IP address argument */
 
        EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */
        EMIT1(0x53);             /* push rbx */
 
+       /* Store number of arguments of the traced function:
+        *   mov rax, nr_args
+        *   mov QWORD PTR [rbp - args_off], rax
+        */
+       emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_args);
+       emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -args_off);
+
        if (flags & BPF_TRAMP_F_IP_ARG) {
                /* Store IP address of the traced function:
                 * mov rax, QWORD PTR [rbp + 8]
 
 void bpf_ksym_del(struct bpf_ksym *ksym);
 int bpf_jit_charge_modmem(u32 pages);
 void bpf_jit_uncharge_modmem(u32 pages);
+bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
 #else
 static inline int bpf_trampoline_link_prog(struct bpf_prog *prog,
                                           struct bpf_trampoline *tr)
 {
        return false;
 }
+static inline bool bpf_prog_has_trampoline(const struct bpf_prog *prog)
+{
+       return false;
+}
 #endif
 
 struct bpf_func_info_aux {
 
  *             An integer less than, equal to, or greater than zero
  *             if the first **s1_sz** bytes of **s1** is found to be
  *             less than, to match, or be greater than **s2**.
+ *
+ * long bpf_get_func_arg(void *ctx, u32 n, u64 *value)
+ *     Description
+ *             Get **n**-th argument (zero based) of the traced function (for tracing programs)
+ *             returned in **value**.
+ *
+ *     Return
+ *             0 on success.
+ *             **-EINVAL** if n >= arguments count of traced function.
+ *
+ * long bpf_get_func_ret(void *ctx, u64 *value)
+ *     Description
+ *             Get return value of the traced function (for tracing programs)
+ *             in **value**.
+ *
+ *     Return
+ *             0 on success.
+ *             **-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN.
+ *
+ * long bpf_get_func_arg_cnt(void *ctx)
+ *     Description
+ *             Get number of arguments of the traced function (for tracing programs).
+ *
+ *     Return
+ *             The number of arguments of the traced function.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
        FN(find_vma),                   \
        FN(loop),                       \
        FN(strncmp),                    \
+       FN(get_func_arg),               \
+       FN(get_func_ret),               \
+       FN(get_func_arg_cnt),           \
        /* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
 
 /* serializes access to trampoline_table */
 static DEFINE_MUTEX(trampoline_mutex);
 
+bool bpf_prog_has_trampoline(const struct bpf_prog *prog)
+{
+       enum bpf_attach_type eatype = prog->expected_attach_type;
+
+       return eatype == BPF_TRACE_FENTRY || eatype == BPF_TRACE_FEXIT ||
+              eatype == BPF_MODIFY_RETURN;
+}
+
 void *bpf_jit_alloc_exec_page(void)
 {
        void *image;
 
 
 static int check_get_func_ip(struct bpf_verifier_env *env)
 {
-       enum bpf_attach_type eatype = env->prog->expected_attach_type;
        enum bpf_prog_type type = resolve_prog_type(env->prog);
        int func_id = BPF_FUNC_get_func_ip;
 
        if (type == BPF_PROG_TYPE_TRACING) {
-               if (eatype != BPF_TRACE_FENTRY && eatype != BPF_TRACE_FEXIT &&
-                   eatype != BPF_MODIFY_RETURN) {
+               if (!bpf_prog_has_trampoline(env->prog)) {
                        verbose(env, "func %s#%d supported only for fentry/fexit/fmod_ret programs\n",
                                func_id_name(func_id), func_id);
                        return -ENOTSUPP;
 static int do_misc_fixups(struct bpf_verifier_env *env)
 {
        struct bpf_prog *prog = env->prog;
+       enum bpf_attach_type eatype = prog->expected_attach_type;
        bool expect_blinding = bpf_jit_blinding_enabled(prog);
        enum bpf_prog_type prog_type = resolve_prog_type(prog);
        struct bpf_insn *insn = prog->insnsi;
                        continue;
                }
 
+               /* Implement bpf_get_func_arg inline. */
+               if (prog_type == BPF_PROG_TYPE_TRACING &&
+                   insn->imm == BPF_FUNC_get_func_arg) {
+                       /* Load nr_args from ctx - 8 */
+                       insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -8);
+                       insn_buf[1] = BPF_JMP32_REG(BPF_JGE, BPF_REG_2, BPF_REG_0, 6);
+                       insn_buf[2] = BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 3);
+                       insn_buf[3] = BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_1);
+                       insn_buf[4] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 0);
+                       insn_buf[5] = BPF_STX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0);
+                       insn_buf[6] = BPF_MOV64_IMM(BPF_REG_0, 0);
+                       insn_buf[7] = BPF_JMP_A(1);
+                       insn_buf[8] = BPF_MOV64_IMM(BPF_REG_0, -EINVAL);
+                       cnt = 9;
+
+                       new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+                       if (!new_prog)
+                               return -ENOMEM;
+
+                       delta    += cnt - 1;
+                       env->prog = prog = new_prog;
+                       insn      = new_prog->insnsi + i + delta;
+                       continue;
+               }
+
+               /* Implement bpf_get_func_ret inline. */
+               if (prog_type == BPF_PROG_TYPE_TRACING &&
+                   insn->imm == BPF_FUNC_get_func_ret) {
+                       if (eatype == BPF_TRACE_FEXIT ||
+                           eatype == BPF_MODIFY_RETURN) {
+                               /* Load nr_args from ctx - 8 */
+                               insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -8);
+                               insn_buf[1] = BPF_ALU64_IMM(BPF_LSH, BPF_REG_0, 3);
+                               insn_buf[2] = BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1);
+                               insn_buf[3] = BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0);
+                               insn_buf[4] = BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0);
+                               insn_buf[5] = BPF_MOV64_IMM(BPF_REG_0, 0);
+                               cnt = 6;
+                       } else {
+                               insn_buf[0] = BPF_MOV64_IMM(BPF_REG_0, -EOPNOTSUPP);
+                               cnt = 1;
+                       }
+
+                       new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+                       if (!new_prog)
+                               return -ENOMEM;
+
+                       delta    += cnt - 1;
+                       env->prog = prog = new_prog;
+                       insn      = new_prog->insnsi + i + delta;
+                       continue;
+               }
+
+               /* Implement get_func_arg_cnt inline. */
+               if (prog_type == BPF_PROG_TYPE_TRACING &&
+                   insn->imm == BPF_FUNC_get_func_arg_cnt) {
+                       /* Load nr_args from ctx - 8 */
+                       insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -8);
+
+                       new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);
+                       if (!new_prog)
+                               return -ENOMEM;
+
+                       env->prog = prog = new_prog;
+                       insn      = new_prog->insnsi + i + delta;
+                       continue;
+               }
+
                /* Implement bpf_get_func_ip inline. */
                if (prog_type == BPF_PROG_TYPE_TRACING &&
                    insn->imm == BPF_FUNC_get_func_ip) {
-                       /* Load IP address from ctx - 8 */
-                       insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -8);
+                       /* Load IP address from ctx - 16 */
+                       insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -16);
 
                        new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);
                        if (!new_prog)
 
 BPF_CALL_1(bpf_get_func_ip_tracing, void *, ctx)
 {
        /* This helper call is inlined by verifier. */
-       return ((u64 *)ctx)[-1];
+       return ((u64 *)ctx)[-2];
 }
 
 static const struct bpf_func_proto bpf_get_func_ip_proto_tracing = {
        .arg2_type      = ARG_CONST_SIZE_OR_ZERO,
 };
 
+BPF_CALL_3(get_func_arg, void *, ctx, u32, n, u64 *, value)
+{
+       /* This helper call is inlined by verifier. */
+       u64 nr_args = ((u64 *)ctx)[-1];
+
+       if ((u64) n >= nr_args)
+               return -EINVAL;
+       *value = ((u64 *)ctx)[n];
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_get_func_arg_proto = {
+       .func           = get_func_arg,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_PTR_TO_LONG,
+};
+
+BPF_CALL_2(get_func_ret, void *, ctx, u64 *, value)
+{
+       /* This helper call is inlined by verifier. */
+       u64 nr_args = ((u64 *)ctx)[-1];
+
+       *value = ((u64 *)ctx)[nr_args];
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_get_func_ret_proto = {
+       .func           = get_func_ret,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_LONG,
+};
+
+BPF_CALL_1(get_func_arg_cnt, void *, ctx)
+{
+       /* This helper call is inlined by verifier. */
+       return ((u64 *)ctx)[-1];
+}
+
+static const struct bpf_func_proto bpf_get_func_arg_cnt_proto = {
+       .func           = get_func_arg_cnt,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+};
+
 static const struct bpf_func_proto *
 bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
                       NULL;
        case BPF_FUNC_d_path:
                return &bpf_d_path_proto;
+       case BPF_FUNC_get_func_arg:
+               return bpf_prog_has_trampoline(prog) ? &bpf_get_func_arg_proto : NULL;
+       case BPF_FUNC_get_func_ret:
+               return bpf_prog_has_trampoline(prog) ? &bpf_get_func_ret_proto : NULL;
+       case BPF_FUNC_get_func_arg_cnt:
+               return bpf_prog_has_trampoline(prog) ? &bpf_get_func_arg_cnt_proto : NULL;
        default:
                fn = raw_tp_prog_func_proto(func_id, prog);
                if (!fn && prog->expected_attach_type == BPF_TRACE_ITER)
 
  *             An integer less than, equal to, or greater than zero
  *             if the first **s1_sz** bytes of **s1** is found to be
  *             less than, to match, or be greater than **s2**.
+ *
+ * long bpf_get_func_arg(void *ctx, u32 n, u64 *value)
+ *     Description
+ *             Get **n**-th argument (zero based) of the traced function (for tracing programs)
+ *             returned in **value**.
+ *
+ *     Return
+ *             0 on success.
+ *             **-EINVAL** if n >= arguments count of traced function.
+ *
+ * long bpf_get_func_ret(void *ctx, u64 *value)
+ *     Description
+ *             Get return value of the traced function (for tracing programs)
+ *             in **value**.
+ *
+ *     Return
+ *             0 on success.
+ *             **-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN.
+ *
+ * long bpf_get_func_arg_cnt(void *ctx)
+ *     Description
+ *             Get number of arguments of the traced function (for tracing programs).
+ *
+ *     Return
+ *             The number of arguments of the traced function.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
        FN(find_vma),                   \
        FN(loop),                       \
        FN(strncmp),                    \
+       FN(get_func_arg),               \
+       FN(get_func_ret),               \
+       FN(get_func_arg_cnt),           \
        /* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper