Commit ca5d1a7f authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'bpf_line_info'

Martin Lau says:

====================
This patch series introduces the bpf_line_info.  Please see individual patch
for details.

It will be useful for introspection purpose, like:

[root@arch-fb-vm1 bpf]# ~/devshare/fb-kernel/linux/tools/bpf/bpftool/bpftool prog dump jited pinned /sys/fs/bpf/test_btf_haskv
[...]
int test_long_fname_2(struct dummy_tracepoint_args * arg):
bpf_prog_44a040bf25481309_test_long_fname_2:
; static int test_long_fname_2(struct dummy_tracepoint_args *arg)
   0:   push   %rbp
   1:   mov    %rsp,%rbp
   4:   sub    $0x30,%rsp
   b:   sub    $0x28,%rbp
   f:   mov    %rbx,0x0(%rbp)
  13:   mov    %r13,0x8(%rbp)
  17:   mov    %r14,0x10(%rbp)
  1b:   mov    %r15,0x18(%rbp)
  1f:   xor    %eax,%eax
  21:   mov    %rax,0x20(%rbp)
  25:   xor    %esi,%esi
; int key = 0;
  27:   mov    %esi,-0x4(%rbp)
; if (!arg->sock)
  2a:   mov    0x8(%rdi),%rdi
; if (!arg->sock)
  2e:   cmp    $0x0,%rdi
  32:   je     0x0000000000000070
  34:   mov    %rbp,%rsi
; counts = bpf_map_lookup_elem(&btf_map, &key);
  37:   add    $0xfffffffffffffffc,%rsi
  3b:   movabs $0xffff8881139d7480,%rdi
  45:   add    $0x110,%rdi
  4c:   mov    0x0(%rsi),%eax
  4f:   cmp    $0x4,%rax
  53:   jae    0x000000000000005e
  55:   shl    $0x3,%rax
  59:   add    %rdi,%rax
  5c:   jmp    0x0000000000000060
  5e:   xor    %eax,%eax
; if (!counts)
  60:   cmp    $0x0,%rax
  64:   je     0x0000000000000070
; counts->v6++;
  66:   mov    0x4(%rax),%edi
  69:   add    $0x1,%rdi
  6d:   mov    %edi,0x4(%rax)
  70:   mov    0x0(%rbp),%rbx
  74:   mov    0x8(%rbp),%r13
  78:   mov    0x10(%rbp),%r14
  7c:   mov    0x18(%rbp),%r15
  80:   add    $0x28,%rbp
  84:   leaveq
  85:   retq
[...]
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 6baefa1a b053b439
...@@ -1181,6 +1181,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ...@@ -1181,6 +1181,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
} }
if (!image || !prog->is_func || extra_pass) { if (!image || !prog->is_func || extra_pass) {
if (image)
bpf_prog_fill_jited_linfo(prog, addrs);
out_addrs: out_addrs:
kfree(addrs); kfree(addrs);
kfree(jit_data); kfree(jit_data);
......
...@@ -319,7 +319,28 @@ struct bpf_prog_aux { ...@@ -319,7 +319,28 @@ struct bpf_prog_aux {
struct bpf_prog_offload *offload; struct bpf_prog_offload *offload;
struct btf *btf; struct btf *btf;
struct bpf_func_info *func_info; struct bpf_func_info *func_info;
/* bpf_line_info loaded from userspace. linfo->insn_off
* has the xlated insn offset.
* Both the main and sub prog share the same linfo.
* The subprog can access its first linfo by
* using the linfo_idx.
*/
struct bpf_line_info *linfo;
/* jited_linfo is the jited addr of the linfo. It has a
* one to one mapping to linfo:
* jited_linfo[i] is the jited addr for the linfo[i]->insn_off.
* Both the main and sub prog share the same jited_linfo.
* The subprog can access its first jited_linfo by
* using the linfo_idx.
*/
void **jited_linfo;
u32 func_info_cnt; u32 func_info_cnt;
u32 nr_linfo;
/* subprog can use linfo_idx to access its first linfo and
* jited_linfo.
* main prog always has linfo_idx == 0
*/
u32 linfo_idx;
union { union {
struct work_struct work; struct work_struct work;
struct rcu_head rcu; struct rcu_head rcu;
......
...@@ -203,6 +203,7 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log) ...@@ -203,6 +203,7 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
struct bpf_subprog_info { struct bpf_subprog_info {
u32 start; /* insn idx of function entry point */ u32 start; /* insn idx of function entry point */
u32 linfo_idx; /* The idx to the main_prog->aux->linfo */
u16 stack_depth; /* max. stack depth used by this function */ u16 stack_depth; /* max. stack depth used by this function */
}; };
......
...@@ -46,6 +46,7 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj, ...@@ -46,6 +46,7 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
struct seq_file *m); struct seq_file *m);
int btf_get_fd_by_id(u32 id); int btf_get_fd_by_id(u32 id);
u32 btf_id(const struct btf *btf); u32 btf_id(const struct btf *btf);
bool btf_name_offset_valid(const struct btf *btf, u32 offset);
#ifdef CONFIG_BPF_SYSCALL #ifdef CONFIG_BPF_SYSCALL
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
......
...@@ -718,6 +718,13 @@ void bpf_prog_free(struct bpf_prog *fp); ...@@ -718,6 +718,13 @@ void bpf_prog_free(struct bpf_prog *fp);
bool bpf_opcode_in_insntable(u8 code); bool bpf_opcode_in_insntable(u8 code);
void bpf_prog_free_linfo(struct bpf_prog *prog);
void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,
const u32 *insn_to_jit_off);
int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog);
void bpf_prog_free_jited_linfo(struct bpf_prog *prog);
void bpf_prog_free_unused_jited_linfo(struct bpf_prog *prog);
struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags); struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags);
struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size, struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
gfp_t gfp_extra_flags); gfp_t gfp_extra_flags);
......
...@@ -356,6 +356,9 @@ union bpf_attr { ...@@ -356,6 +356,9 @@ union bpf_attr {
__u32 func_info_rec_size; /* userspace bpf_func_info size */ __u32 func_info_rec_size; /* userspace bpf_func_info size */
__aligned_u64 func_info; /* func info */ __aligned_u64 func_info; /* func info */
__u32 func_info_cnt; /* number of bpf_func_info records */ __u32 func_info_cnt; /* number of bpf_func_info records */
__u32 line_info_rec_size; /* userspace bpf_line_info size */
__aligned_u64 line_info; /* line info */
__u32 line_info_cnt; /* number of bpf_line_info records */
}; };
struct { /* anonymous struct used by BPF_OBJ_* commands */ struct { /* anonymous struct used by BPF_OBJ_* commands */
...@@ -2679,6 +2682,12 @@ struct bpf_prog_info { ...@@ -2679,6 +2682,12 @@ struct bpf_prog_info {
__u32 func_info_rec_size; __u32 func_info_rec_size;
__aligned_u64 func_info; __aligned_u64 func_info;
__u32 func_info_cnt; __u32 func_info_cnt;
__u32 line_info_cnt;
__aligned_u64 line_info;
__aligned_u64 jited_line_info;
__u32 jited_line_info_cnt;
__u32 line_info_rec_size;
__u32 jited_line_info_rec_size;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_map_info { struct bpf_map_info {
...@@ -2995,4 +3004,14 @@ struct bpf_func_info { ...@@ -2995,4 +3004,14 @@ struct bpf_func_info {
__u32 type_id; __u32 type_id;
}; };
#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10)
#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff)
struct bpf_line_info {
__u32 insn_off;
__u32 file_name_off;
__u32 line_off;
__u32 line_col;
};
#endif /* _UAPI__LINUX_BPF_H__ */ #endif /* _UAPI__LINUX_BPF_H__ */
...@@ -444,7 +444,7 @@ static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t) ...@@ -444,7 +444,7 @@ static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
return kind_ops[BTF_INFO_KIND(t->info)]; return kind_ops[BTF_INFO_KIND(t->info)];
} }
static bool btf_name_offset_valid(const struct btf *btf, u32 offset) bool btf_name_offset_valid(const struct btf *btf, u32 offset)
{ {
return BTF_STR_OFFSET_VALID(offset) && return BTF_STR_OFFSET_VALID(offset) &&
offset < btf->hdr.str_len; offset < btf->hdr.str_len;
......
...@@ -105,6 +105,91 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags) ...@@ -105,6 +105,91 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
} }
EXPORT_SYMBOL_GPL(bpf_prog_alloc); EXPORT_SYMBOL_GPL(bpf_prog_alloc);
int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog)
{
if (!prog->aux->nr_linfo || !prog->jit_requested)
return 0;
prog->aux->jited_linfo = kcalloc(prog->aux->nr_linfo,
sizeof(*prog->aux->jited_linfo),
GFP_KERNEL | __GFP_NOWARN);
if (!prog->aux->jited_linfo)
return -ENOMEM;
return 0;
}
void bpf_prog_free_jited_linfo(struct bpf_prog *prog)
{
kfree(prog->aux->jited_linfo);
prog->aux->jited_linfo = NULL;
}
void bpf_prog_free_unused_jited_linfo(struct bpf_prog *prog)
{
if (prog->aux->jited_linfo && !prog->aux->jited_linfo[0])
bpf_prog_free_jited_linfo(prog);
}
/* The jit engine is responsible to provide an array
* for insn_off to the jited_off mapping (insn_to_jit_off).
*
* The idx to this array is the insn_off. Hence, the insn_off
* here is relative to the prog itself instead of the main prog.
* This array has one entry for each xlated bpf insn.
*
* jited_off is the byte off to the last byte of the jited insn.
*
* Hence, with
* insn_start:
* The first bpf insn off of the prog. The insn off
* here is relative to the main prog.
* e.g. if prog is a subprog, insn_start > 0
* linfo_idx:
* The prog's idx to prog->aux->linfo and jited_linfo
*
* jited_linfo[linfo_idx] = prog->bpf_func
*
* For i > linfo_idx,
*
* jited_linfo[i] = prog->bpf_func +
* insn_to_jit_off[linfo[i].insn_off - insn_start - 1]
*/
void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,
const u32 *insn_to_jit_off)
{
u32 linfo_idx, insn_start, insn_end, nr_linfo, i;
const struct bpf_line_info *linfo;
void **jited_linfo;
if (!prog->aux->jited_linfo)
/* Userspace did not provide linfo */
return;
linfo_idx = prog->aux->linfo_idx;
linfo = &prog->aux->linfo[linfo_idx];
insn_start = linfo[0].insn_off;
insn_end = insn_start + prog->len;
jited_linfo = &prog->aux->jited_linfo[linfo_idx];
jited_linfo[0] = prog->bpf_func;
nr_linfo = prog->aux->nr_linfo - linfo_idx;
for (i = 1; i < nr_linfo && linfo[i].insn_off < insn_end; i++)
/* The verifier ensures that linfo[i].insn_off is
* strictly increasing
*/
jited_linfo[i] = prog->bpf_func +
insn_to_jit_off[linfo[i].insn_off - insn_start - 1];
}
void bpf_prog_free_linfo(struct bpf_prog *prog)
{
bpf_prog_free_jited_linfo(prog);
kvfree(prog->aux->linfo);
}
struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size, struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
gfp_t gfp_extra_flags) gfp_t gfp_extra_flags)
{ {
...@@ -294,6 +379,26 @@ static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta, ...@@ -294,6 +379,26 @@ static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
return ret; return ret;
} }
static void bpf_adj_linfo(struct bpf_prog *prog, u32 off, u32 delta)
{
struct bpf_line_info *linfo;
u32 i, nr_linfo;
nr_linfo = prog->aux->nr_linfo;
if (!nr_linfo || !delta)
return;
linfo = prog->aux->linfo;
for (i = 0; i < nr_linfo; i++)
if (off < linfo[i].insn_off)
break;
/* Push all off < linfo[i].insn_off by delta */
for (; i < nr_linfo; i++)
linfo[i].insn_off += delta;
}
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
const struct bpf_insn *patch, u32 len) const struct bpf_insn *patch, u32 len)
{ {
...@@ -349,6 +454,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, ...@@ -349,6 +454,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
*/ */
BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false)); BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
bpf_adj_linfo(prog_adj, off, insn_delta);
return prog_adj; return prog_adj;
} }
...@@ -1591,13 +1698,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) ...@@ -1591,13 +1698,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
* be JITed, but falls back to the interpreter. * be JITed, but falls back to the interpreter.
*/ */
if (!bpf_prog_is_dev_bound(fp->aux)) { if (!bpf_prog_is_dev_bound(fp->aux)) {
*err = bpf_prog_alloc_jited_linfo(fp);
if (*err)
return fp;
fp = bpf_int_jit_compile(fp); fp = bpf_int_jit_compile(fp);
#ifdef CONFIG_BPF_JIT_ALWAYS_ON
if (!fp->jited) { if (!fp->jited) {
bpf_prog_free_jited_linfo(fp);
#ifdef CONFIG_BPF_JIT_ALWAYS_ON
*err = -ENOTSUPP; *err = -ENOTSUPP;
return fp; return fp;
}
#endif #endif
} else {
bpf_prog_free_unused_jited_linfo(fp);
}
} else { } else {
*err = bpf_prog_offload_compile(fp); *err = bpf_prog_offload_compile(fp);
if (*err) if (*err)
......
...@@ -1215,6 +1215,7 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) ...@@ -1215,6 +1215,7 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
bpf_prog_kallsyms_del_all(prog); bpf_prog_kallsyms_del_all(prog);
btf_put(prog->aux->btf); btf_put(prog->aux->btf);
kvfree(prog->aux->func_info); kvfree(prog->aux->func_info);
bpf_prog_free_linfo(prog);
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
} }
...@@ -1439,7 +1440,7 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type, ...@@ -1439,7 +1440,7 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,
} }
/* last field in 'union bpf_attr' used by this command */ /* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD func_info_cnt #define BPF_PROG_LOAD_LAST_FIELD line_info_cnt
static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
{ {
...@@ -1560,6 +1561,7 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) ...@@ -1560,6 +1561,7 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
return err; return err;
free_used_maps: free_used_maps:
bpf_prog_free_linfo(prog);
kvfree(prog->aux->func_info); kvfree(prog->aux->func_info);
btf_put(prog->aux->btf); btf_put(prog->aux->btf);
bpf_prog_kallsyms_del_subprogs(prog); bpf_prog_kallsyms_del_subprogs(prog);
...@@ -2041,6 +2043,37 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog) ...@@ -2041,6 +2043,37 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog)
return insns; return insns;
} }
static int set_info_rec_size(struct bpf_prog_info *info)
{
/*
* Ensure info.*_rec_size is the same as kernel expected size
*
* or
*
* Only allow zero *_rec_size if both _rec_size and _cnt are
* zero. In this case, the kernel will set the expected
* _rec_size back to the info.
*/
if ((info->func_info_cnt || info->func_info_rec_size) &&
info->func_info_rec_size != sizeof(struct bpf_func_info))
return -EINVAL;
if ((info->line_info_cnt || info->line_info_rec_size) &&
info->line_info_rec_size != sizeof(struct bpf_line_info))
return -EINVAL;
if ((info->jited_line_info_cnt || info->jited_line_info_rec_size) &&
info->jited_line_info_rec_size != sizeof(__u64))
return -EINVAL;
info->func_info_rec_size = sizeof(struct bpf_func_info);
info->line_info_rec_size = sizeof(struct bpf_line_info);
info->jited_line_info_rec_size = sizeof(__u64);
return 0;
}
static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
const union bpf_attr *attr, const union bpf_attr *attr,
union bpf_attr __user *uattr) union bpf_attr __user *uattr)
...@@ -2083,11 +2116,9 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, ...@@ -2083,11 +2116,9 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
return -EFAULT; return -EFAULT;
} }
if ((info.func_info_cnt || info.func_info_rec_size) && err = set_info_rec_size(&info);
info.func_info_rec_size != sizeof(struct bpf_func_info)) if (err)
return -EINVAL; return err;
info.func_info_rec_size = sizeof(struct bpf_func_info);
if (!capable(CAP_SYS_ADMIN)) { if (!capable(CAP_SYS_ADMIN)) {
info.jited_prog_len = 0; info.jited_prog_len = 0;
...@@ -2095,6 +2126,8 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, ...@@ -2095,6 +2126,8 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
info.nr_jited_ksyms = 0; info.nr_jited_ksyms = 0;
info.nr_jited_func_lens = 0; info.nr_jited_func_lens = 0;
info.func_info_cnt = 0; info.func_info_cnt = 0;
info.line_info_cnt = 0;
info.jited_line_info_cnt = 0;
goto done; goto done;
} }
...@@ -2251,6 +2284,44 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, ...@@ -2251,6 +2284,44 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
} }
} }
ulen = info.line_info_cnt;
info.line_info_cnt = prog->aux->nr_linfo;
if (info.line_info_cnt && ulen) {
if (bpf_dump_raw_ok()) {
__u8 __user *user_linfo;
user_linfo = u64_to_user_ptr(info.line_info);
ulen = min_t(u32, info.line_info_cnt, ulen);
if (copy_to_user(user_linfo, prog->aux->linfo,
info.line_info_rec_size * ulen))
return -EFAULT;
} else {
info.line_info = 0;
}
}
ulen = info.jited_line_info_cnt;
if (prog->aux->jited_linfo)
info.jited_line_info_cnt = prog->aux->nr_linfo;
else
info.jited_line_info_cnt = 0;
if (info.jited_line_info_cnt && ulen) {
if (bpf_dump_raw_ok()) {
__u64 __user *user_linfo;
u32 i;
user_linfo = u64_to_user_ptr(info.jited_line_info);
ulen = min_t(u32, info.jited_line_info_cnt, ulen);
for (i = 0; i < ulen; i++) {
if (put_user((__u64)(long)prog->aux->jited_linfo[i],
&user_linfo[i]))
return -EFAULT;
}
} else {
info.jited_line_info = 0;
}
}
done: done:
if (copy_to_user(uinfo, &info, info_len) || if (copy_to_user(uinfo, &info, info_len) ||
put_user(info_len, &uattr->info.info_len)) put_user(info_len, &uattr->info.info_len))
......
...@@ -4640,15 +4640,17 @@ static int check_cfg(struct bpf_verifier_env *env) ...@@ -4640,15 +4640,17 @@ static int check_cfg(struct bpf_verifier_env *env)
#define MIN_BPF_FUNCINFO_SIZE 8 #define MIN_BPF_FUNCINFO_SIZE 8
#define MAX_FUNCINFO_REC_SIZE 252 #define MAX_FUNCINFO_REC_SIZE 252
static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env, static int check_btf_func(struct bpf_verifier_env *env,
union bpf_attr *attr, union bpf_attr __user *uattr) const union bpf_attr *attr,
union bpf_attr __user *uattr)
{ {
u32 i, nfuncs, urec_size, min_size, prev_offset; u32 i, nfuncs, urec_size, min_size, prev_offset;
u32 krec_size = sizeof(struct bpf_func_info); u32 krec_size = sizeof(struct bpf_func_info);
struct bpf_func_info *krecord = NULL; struct bpf_func_info *krecord;
const struct btf_type *type; const struct btf_type *type;
struct bpf_prog *prog;
const struct btf *btf;
void __user *urecord; void __user *urecord;
struct btf *btf;
int ret = 0; int ret = 0;
nfuncs = attr->func_info_cnt; nfuncs = attr->func_info_cnt;
...@@ -4668,20 +4670,15 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env, ...@@ -4668,20 +4670,15 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env,
return -EINVAL; return -EINVAL;
} }
btf = btf_get_by_fd(attr->prog_btf_fd); prog = env->prog;
if (IS_ERR(btf)) { btf = prog->aux->btf;
verbose(env, "unable to get btf from fd\n");
return PTR_ERR(btf);
}
urecord = u64_to_user_ptr(attr->func_info); urecord = u64_to_user_ptr(attr->func_info);
min_size = min_t(u32, krec_size, urec_size); min_size = min_t(u32, krec_size, urec_size);
krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN); krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN);
if (!krecord) { if (!krecord)
ret = -ENOMEM; return -ENOMEM;
goto free_btf;
}
for (i = 0; i < nfuncs; i++) { for (i = 0; i < nfuncs; i++) {
ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size); ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size);
...@@ -4694,12 +4691,12 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env, ...@@ -4694,12 +4691,12 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env,
if (put_user(min_size, &uattr->func_info_rec_size)) if (put_user(min_size, &uattr->func_info_rec_size))
ret = -EFAULT; ret = -EFAULT;
} }
goto free_btf; goto err_free;
} }
if (copy_from_user(&krecord[i], urecord, min_size)) { if (copy_from_user(&krecord[i], urecord, min_size)) {
ret = -EFAULT; ret = -EFAULT;
goto free_btf; goto err_free;
} }
/* check insn_off */ /* check insn_off */
...@@ -4709,20 +4706,20 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env, ...@@ -4709,20 +4706,20 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env,
"nonzero insn_off %u for the first func info record", "nonzero insn_off %u for the first func info record",
krecord[i].insn_off); krecord[i].insn_off);
ret = -EINVAL; ret = -EINVAL;
goto free_btf; goto err_free;
} }
} else if (krecord[i].insn_off <= prev_offset) { } else if (krecord[i].insn_off <= prev_offset) {
verbose(env, verbose(env,
"same or smaller insn offset (%u) than previous func info record (%u)", "same or smaller insn offset (%u) than previous func info record (%u)",
krecord[i].insn_off, prev_offset); krecord[i].insn_off, prev_offset);
ret = -EINVAL; ret = -EINVAL;
goto free_btf; goto err_free;
} }
if (env->subprog_info[i].start != krecord[i].insn_off) { if (env->subprog_info[i].start != krecord[i].insn_off) {
verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n"); verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n");
ret = -EINVAL; ret = -EINVAL;
goto free_btf; goto err_free;
} }
/* check type_id */ /* check type_id */
...@@ -4731,20 +4728,18 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env, ...@@ -4731,20 +4728,18 @@ static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env,
verbose(env, "invalid type id %d in func info", verbose(env, "invalid type id %d in func info",
krecord[i].type_id); krecord[i].type_id);
ret = -EINVAL; ret = -EINVAL;
goto free_btf; goto err_free;
} }
prev_offset = krecord[i].insn_off; prev_offset = krecord[i].insn_off;
urecord += urec_size; urecord += urec_size;
} }
prog->aux->btf = btf;
prog->aux->func_info = krecord; prog->aux->func_info = krecord;
prog->aux->func_info_cnt = nfuncs; prog->aux->func_info_cnt = nfuncs;
return 0; return 0;
free_btf: err_free:
btf_put(btf);
kvfree(krecord); kvfree(krecord);
return ret; return ret;
} }
...@@ -4760,6 +4755,150 @@ static void adjust_btf_func(struct bpf_verifier_env *env) ...@@ -4760,6 +4755,150 @@ static void adjust_btf_func(struct bpf_verifier_env *env)
env->prog->aux->func_info[i].insn_off = env->subprog_info[i].start; env->prog->aux->func_info[i].insn_off = env->subprog_info[i].start;
} }
#define MIN_BPF_LINEINFO_SIZE (offsetof(struct bpf_line_info, line_col) + \
sizeof(((struct bpf_line_info *)(0))->line_col))
#define MAX_LINEINFO_REC_SIZE MAX_FUNCINFO_REC_SIZE
static int check_btf_line(struct bpf_verifier_env *env,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
{
u32 i, s, nr_linfo, ncopy, expected_size, rec_size, prev_offset = 0;
struct bpf_subprog_info *sub;
struct bpf_line_info *linfo;
struct bpf_prog *prog;
const struct btf *btf;
void __user *ulinfo;
int err;
nr_linfo = attr->line_info_cnt;
if (!nr_linfo)
return 0;
rec_size = attr->line_info_rec_size;
if (rec_size < MIN_BPF_LINEINFO_SIZE ||
rec_size > MAX_LINEINFO_REC_SIZE ||
rec_size & (sizeof(u32) - 1))
return -EINVAL;
/* Need to zero it in case the userspace may
* pass in a smaller bpf_line_info object.
*/
linfo = kvcalloc(nr_linfo, sizeof(struct bpf_line_info),
GFP_KERNEL | __GFP_NOWARN);
if (!linfo)
return -ENOMEM;
prog = env->prog;
btf = prog->aux->btf;
s = 0;
sub = env->subprog_info;
ulinfo = u64_to_user_ptr(attr->line_info);
expected_size = sizeof(struct bpf_line_info);
ncopy = min_t(u32, expected_size, rec_size);
for (i = 0; i < nr_linfo; i++) {
err = bpf_check_uarg_tail_zero(ulinfo, expected_size, rec_size);
if (err) {
if (err == -E2BIG) {
verbose(env, "nonzero tailing record in line_info");
if (put_user(expected_size,
&uattr->line_info_rec_size))
err = -EFAULT;
}
goto err_free;
}
if (copy_from_user(&linfo[i], ulinfo, ncopy)) {
err = -EFAULT;
goto err_free;
}
/*
* Check insn_off to ensure
* 1) strictly increasing AND
* 2) bounded by prog->len
*
* The linfo[0].insn_off == 0 check logically falls into
* the later "missing bpf_line_info for func..." case
* because the first linfo[0].insn_off must be the
* first sub also and the first sub must have
* subprog_info[0].start == 0.
*/
if ((i && linfo[i].insn_off <= prev_offset) ||
linfo[i].insn_off >= prog->len) {
verbose(env, "Invalid line_info[%u].insn_off:%u (prev_offset:%u prog->len:%u)\n",
i, linfo[i].insn_off, prev_offset,
prog->len);
err = -EINVAL;
goto err_free;
}
if (!btf_name_offset_valid(btf, linfo[i].line_off) ||
!btf_name_offset_valid(btf, linfo[i].file_name_off)) {
verbose(env, "Invalid line_info[%u].line_off or .file_name_off\n", i);
err = -EINVAL;
goto err_free;
}
if (s != env->subprog_cnt) {
if (linfo[i].insn_off == sub[s].start) {
sub[s].linfo_idx = i;
s++;
} else if (sub[s].start < linfo[i].insn_off) {
verbose(env, "missing bpf_line_info for func#%u\n", s);
err = -EINVAL;
goto err_free;
}
}
prev_offset = linfo[i].insn_off;
ulinfo += rec_size;
}
if (s != env->subprog_cnt) {
verbose(env, "missing bpf_line_info for %u funcs starting from func#%u\n",
env->subprog_cnt - s, s);
err = -EINVAL;
goto err_free;
}
prog->aux->linfo = linfo;
prog->aux->nr_linfo = nr_linfo;
return 0;
err_free:
kvfree(linfo);
return err;
}
static int check_btf_info(struct bpf_verifier_env *env,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
{
struct btf *btf;
int err;
if (!attr->func_info_cnt && !attr->line_info_cnt)
return 0;
btf = btf_get_by_fd(attr->prog_btf_fd);
if (IS_ERR(btf))
return PTR_ERR(btf);
env->prog->aux->btf = btf;
err = check_btf_func(env, attr, uattr);
if (err)
return err;
err = check_btf_line(env, attr, uattr);
if (err)
return err;
return 0;
}
/* check %cur's range satisfies %old's */ /* check %cur's range satisfies %old's */
static bool range_within(struct bpf_reg_state *old, static bool range_within(struct bpf_reg_state *old,
struct bpf_reg_state *cur) struct bpf_reg_state *cur)
...@@ -6004,7 +6143,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) ...@@ -6004,7 +6143,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
int i, j, subprog_start, subprog_end = 0, len, subprog; int i, j, subprog_start, subprog_end = 0, len, subprog;
struct bpf_insn *insn; struct bpf_insn *insn;
void *old_bpf_func; void *old_bpf_func;
int err = -ENOMEM; int err;
if (env->subprog_cnt <= 1) if (env->subprog_cnt <= 1)
return 0; return 0;
...@@ -6035,6 +6174,11 @@ static int jit_subprogs(struct bpf_verifier_env *env) ...@@ -6035,6 +6174,11 @@ static int jit_subprogs(struct bpf_verifier_env *env)
insn->imm = 1; insn->imm = 1;
} }
err = bpf_prog_alloc_jited_linfo(prog);
if (err)
goto out_undo_insn;
err = -ENOMEM;
func = kcalloc(env->subprog_cnt, sizeof(prog), GFP_KERNEL); func = kcalloc(env->subprog_cnt, sizeof(prog), GFP_KERNEL);
if (!func) if (!func)
goto out_undo_insn; goto out_undo_insn;
...@@ -6065,6 +6209,10 @@ static int jit_subprogs(struct bpf_verifier_env *env) ...@@ -6065,6 +6209,10 @@ static int jit_subprogs(struct bpf_verifier_env *env)
func[i]->aux->name[0] = 'F'; func[i]->aux->name[0] = 'F';
func[i]->aux->stack_depth = env->subprog_info[i].stack_depth; func[i]->aux->stack_depth = env->subprog_info[i].stack_depth;
func[i]->jit_requested = 1; func[i]->jit_requested = 1;
func[i]->aux->linfo = prog->aux->linfo;
func[i]->aux->nr_linfo = prog->aux->nr_linfo;
func[i]->aux->jited_linfo = prog->aux->jited_linfo;
func[i]->aux->linfo_idx = env->subprog_info[i].linfo_idx;
func[i] = bpf_int_jit_compile(func[i]); func[i] = bpf_int_jit_compile(func[i]);
if (!func[i]->jited) { if (!func[i]->jited) {
err = -ENOTSUPP; err = -ENOTSUPP;
...@@ -6138,6 +6286,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) ...@@ -6138,6 +6286,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
prog->bpf_func = func[0]->bpf_func; prog->bpf_func = func[0]->bpf_func;
prog->aux->func = func; prog->aux->func = func;
prog->aux->func_cnt = env->subprog_cnt; prog->aux->func_cnt = env->subprog_cnt;
bpf_prog_free_unused_jited_linfo(prog);
return 0; return 0;
out_free: out_free:
for (i = 0; i < env->subprog_cnt; i++) for (i = 0; i < env->subprog_cnt; i++)
...@@ -6154,6 +6303,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) ...@@ -6154,6 +6303,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
insn->off = 0; insn->off = 0;
insn->imm = env->insn_aux_data[i].call_imm; insn->imm = env->insn_aux_data[i].call_imm;
} }
bpf_prog_free_jited_linfo(prog);
return err; return err;
} }
...@@ -6526,7 +6676,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, ...@@ -6526,7 +6676,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
if (ret < 0) if (ret < 0)
goto skip_full_check; goto skip_full_check;
ret = check_btf_func(env->prog, env, attr, uattr); ret = check_btf_info(env, attr, uattr);
if (ret < 0) if (ret < 0)
goto skip_full_check; goto skip_full_check;
......
...@@ -22,8 +22,8 @@ MAP COMMANDS ...@@ -22,8 +22,8 @@ MAP COMMANDS
============= =============
| **bpftool** **prog { show | list }** [*PROG*] | **bpftool** **prog { show | list }** [*PROG*]
| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}] | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual** | **linum**}]
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes** | **linum**}]
| **bpftool** **prog pin** *PROG* *FILE* | **bpftool** **prog pin** *PROG* *FILE*
| **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] | **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
...@@ -56,7 +56,7 @@ DESCRIPTION ...@@ -56,7 +56,7 @@ DESCRIPTION
Output will start with program ID followed by program type and Output will start with program ID followed by program type and
zero or more named attributes (depending on kernel version). zero or more named attributes (depending on kernel version).
**bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** }] **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** | **linum** }]
Dump eBPF instructions of the program from the kernel. By Dump eBPF instructions of the program from the kernel. By
default, eBPF will be disassembled and printed to standard default, eBPF will be disassembled and printed to standard
output in human-readable format. In this case, **opcodes** output in human-readable format. In this case, **opcodes**
...@@ -69,13 +69,21 @@ DESCRIPTION ...@@ -69,13 +69,21 @@ DESCRIPTION
built instead, and eBPF instructions will be presented with built instead, and eBPF instructions will be presented with
CFG in DOT format, on standard output. CFG in DOT format, on standard output.
**bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** }] If the prog has line_info available, the source line will
be displayed by default. If **linum** is specified,
the filename, line number and line column will also be
displayed on top of the source line.
**bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** | **linum** }]
Dump jited image (host machine code) of the program. Dump jited image (host machine code) of the program.
If *FILE* is specified image will be written to a file, If *FILE* is specified image will be written to a file,
otherwise it will be disassembled and printed to stdout. otherwise it will be disassembled and printed to stdout.
**opcodes** controls if raw opcodes will be printed. **opcodes** controls if raw opcodes will be printed.
If the prog has line_info available, the source line will
be displayed by default. If **linum** is specified,
the filename, line number and line column will also be
displayed on top of the source line.
**bpftool prog pin** *PROG* *FILE* **bpftool prog pin** *PROG* *FILE*
Pin program *PROG* as *FILE*. Pin program *PROG* as *FILE*.
......
...@@ -191,7 +191,7 @@ _bpftool() ...@@ -191,7 +191,7 @@ _bpftool()
# Deal with simplest keywords # Deal with simplest keywords
case $prev in case $prev in
help|hex|opcodes|visual) help|hex|opcodes|visual|linum)
return 0 return 0
;; ;;
tag) tag)
...@@ -278,10 +278,10 @@ _bpftool() ...@@ -278,10 +278,10 @@ _bpftool()
*) *)
_bpftool_once_attr 'file' _bpftool_once_attr 'file'
if _bpftool_search_list 'xlated'; then if _bpftool_search_list 'xlated'; then
COMPREPLY+=( $( compgen -W 'opcodes visual' -- \ COMPREPLY+=( $( compgen -W 'opcodes visual linum' -- \
"$cur" ) ) "$cur" ) )
else else
COMPREPLY+=( $( compgen -W 'opcodes' -- \ COMPREPLY+=( $( compgen -W 'opcodes linum' -- \
"$cur" ) ) "$cur" ) )
fi fi
return 0 return 0
......
...@@ -385,3 +385,67 @@ void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig, ...@@ -385,3 +385,67 @@ void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig,
if (err < 0) if (err < 0)
func_sig[0] = '\0'; func_sig[0] = '\0';
} }
static const char *ltrim(const char *s)
{
while (isspace(*s))
s++;
return s;
}
void btf_dump_linfo_plain(const struct btf *btf,
const struct bpf_line_info *linfo,
const char *prefix, bool linum)
{
const char *line = btf__name_by_offset(btf, linfo->line_off);
if (!line)
return;
line = ltrim(line);
if (!prefix)
prefix = "";
if (linum) {
const char *file = btf__name_by_offset(btf, linfo->file_name_off);
/* More forgiving on file because linum option is
* expected to provide more info than the already
* available src line.
*/
if (!file)
file = "";
printf("%s%s [file:%s line_num:%u line_col:%u]\n",
prefix, line, file,
BPF_LINE_INFO_LINE_NUM(linfo->line_col),
BPF_LINE_INFO_LINE_COL(linfo->line_col));
} else {
printf("%s%s\n", prefix, line);
}
}
void btf_dump_linfo_json(const struct btf *btf,
const struct bpf_line_info *linfo, bool linum)
{
const char *line = btf__name_by_offset(btf, linfo->line_off);
if (line)
jsonw_string_field(json_wtr, "src", ltrim(line));
if (linum) {
const char *file = btf__name_by_offset(btf, linfo->file_name_off);
if (file)
jsonw_string_field(json_wtr, "file", file);
if (BPF_LINE_INFO_LINE_NUM(linfo->line_col))
jsonw_int_field(json_wtr, "line_num",
BPF_LINE_INFO_LINE_NUM(linfo->line_col));
if (BPF_LINE_INFO_LINE_COL(linfo->line_col))
jsonw_int_field(json_wtr, "line_col",
BPF_LINE_INFO_LINE_COL(linfo->line_col));
}
}
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <dis-asm.h> #include <dis-asm.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <limits.h> #include <limits.h>
#include <libbpf.h>
#include "json_writer.h" #include "json_writer.h"
#include "main.h" #include "main.h"
...@@ -68,10 +69,16 @@ static int fprintf_json(void *out, const char *fmt, ...) ...@@ -68,10 +69,16 @@ static int fprintf_json(void *out, const char *fmt, ...)
} }
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
const char *arch, const char *disassembler_options) const char *arch, const char *disassembler_options,
const struct btf *btf,
const struct bpf_prog_linfo *prog_linfo,
__u64 func_ksym, unsigned int func_idx,
bool linum)
{ {
const struct bpf_line_info *linfo = NULL;
disassembler_ftype disassemble; disassembler_ftype disassemble;
struct disassemble_info info; struct disassemble_info info;
unsigned int nr_skip = 0;
int count, i, pc = 0; int count, i, pc = 0;
char tpath[PATH_MAX]; char tpath[PATH_MAX];
bfd *bfdf; bfd *bfdf;
...@@ -127,12 +134,26 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, ...@@ -127,12 +134,26 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
if (json_output) if (json_output)
jsonw_start_array(json_wtr); jsonw_start_array(json_wtr);
do { do {
if (prog_linfo) {
linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
func_ksym + pc,
func_idx,
nr_skip);
if (linfo)
nr_skip++;
}
if (json_output) { if (json_output) {
jsonw_start_object(json_wtr); jsonw_start_object(json_wtr);
oper_count = 0; oper_count = 0;
if (linfo)
btf_dump_linfo_json(btf, linfo, linum);
jsonw_name(json_wtr, "pc"); jsonw_name(json_wtr, "pc");
jsonw_printf(json_wtr, "\"0x%x\"", pc); jsonw_printf(json_wtr, "\"0x%x\"", pc);
} else { } else {
if (linfo)
btf_dump_linfo_plain(btf, linfo, "; ",
linum);
printf("%4x:\t", pc); printf("%4x:\t", pc);
} }
......
...@@ -138,6 +138,9 @@ struct pinned_obj { ...@@ -138,6 +138,9 @@ struct pinned_obj {
struct hlist_node hash; struct hlist_node hash;
}; };
struct btf;
struct bpf_line_info;
int build_pinned_obj_table(struct pinned_obj_table *table, int build_pinned_obj_table(struct pinned_obj_table *table,
enum bpf_obj_type type); enum bpf_obj_type type);
void delete_pinned_obj_table(struct pinned_obj_table *tab); void delete_pinned_obj_table(struct pinned_obj_table *tab);
...@@ -175,13 +178,23 @@ int map_parse_fd(int *argc, char ***argv); ...@@ -175,13 +178,23 @@ int map_parse_fd(int *argc, char ***argv);
int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len); int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
#ifdef HAVE_LIBBFD_SUPPORT #ifdef HAVE_LIBBFD_SUPPORT
struct bpf_prog_linfo;
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
const char *arch, const char *disassembler_options); const char *arch, const char *disassembler_options,
const struct btf *btf,
const struct bpf_prog_linfo *prog_linfo,
__u64 func_ksym, unsigned int func_idx,
bool linum);
int disasm_init(void); int disasm_init(void);
#else #else
static inline static inline
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
const char *arch, const char *disassembler_options) const char *arch, const char *disassembler_options,
const struct btf *btf,
const struct bpf_prog_linfo *prog_linfo,
__u64 func_ksym, unsigned int func_idx,
bool linum)
{ {
} }
static inline int disasm_init(void) static inline int disasm_init(void)
...@@ -217,6 +230,12 @@ int btf_dumper_type(const struct btf_dumper *d, __u32 type_id, ...@@ -217,6 +230,12 @@ int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
void btf_dumper_type_only(const struct btf *btf, __u32 func_type_id, void btf_dumper_type_only(const struct btf *btf, __u32 func_type_id,
char *func_only, int size); char *func_only, int size);
void btf_dump_linfo_plain(const struct btf *btf,
const struct bpf_line_info *linfo,
const char *prefix, bool linum);
void btf_dump_linfo_json(const struct btf *btf,
const struct bpf_line_info *linfo, bool linum);
struct nlattr; struct nlattr;
struct ifinfomsg; struct ifinfomsg;
struct tcmsg; struct tcmsg;
......
...@@ -423,24 +423,26 @@ static int do_show(int argc, char **argv) ...@@ -423,24 +423,26 @@ static int do_show(int argc, char **argv)
static int do_dump(int argc, char **argv) static int do_dump(int argc, char **argv)
{ {
unsigned int finfo_rec_size, linfo_rec_size, jited_linfo_rec_size;
void *func_info = NULL, *linfo = NULL, *jited_linfo = NULL;
unsigned int finfo_cnt, linfo_cnt = 0, jited_linfo_cnt = 0;
struct bpf_prog_linfo *prog_linfo = NULL;
unsigned long *func_ksyms = NULL; unsigned long *func_ksyms = NULL;
struct bpf_prog_info info = {}; struct bpf_prog_info info = {};
unsigned int *func_lens = NULL; unsigned int *func_lens = NULL;
const char *disasm_opt = NULL; const char *disasm_opt = NULL;
unsigned int finfo_rec_size;
unsigned int nr_func_ksyms; unsigned int nr_func_ksyms;
unsigned int nr_func_lens; unsigned int nr_func_lens;
struct dump_data dd = {}; struct dump_data dd = {};
__u32 len = sizeof(info); __u32 len = sizeof(info);
struct btf *btf = NULL; struct btf *btf = NULL;
void *func_info = NULL;
unsigned int finfo_cnt;
unsigned int buf_size; unsigned int buf_size;
char *filepath = NULL; char *filepath = NULL;
bool opcodes = false; bool opcodes = false;
bool visual = false; bool visual = false;
char func_sig[1024]; char func_sig[1024];
unsigned char *buf; unsigned char *buf;
bool linum = false;
__u32 *member_len; __u32 *member_len;
__u64 *member_ptr; __u64 *member_ptr;
ssize_t n; ssize_t n;
...@@ -484,6 +486,9 @@ static int do_dump(int argc, char **argv) ...@@ -484,6 +486,9 @@ static int do_dump(int argc, char **argv)
} else if (is_prefix(*argv, "visual")) { } else if (is_prefix(*argv, "visual")) {
visual = true; visual = true;
NEXT_ARG(); NEXT_ARG();
} else if (is_prefix(*argv, "linum")) {
linum = true;
NEXT_ARG();
} }
if (argc) { if (argc) {
...@@ -543,6 +548,32 @@ static int do_dump(int argc, char **argv) ...@@ -543,6 +548,32 @@ static int do_dump(int argc, char **argv)
} }
} }
linfo_rec_size = info.line_info_rec_size;
if (info.line_info_cnt && linfo_rec_size && info.btf_id) {
linfo_cnt = info.line_info_cnt;
linfo = malloc(linfo_cnt * linfo_rec_size);
if (!linfo) {
p_err("mem alloc failed");
close(fd);
goto err_free;
}
}
jited_linfo_rec_size = info.jited_line_info_rec_size;
if (info.jited_line_info_cnt &&
jited_linfo_rec_size &&
info.nr_jited_ksyms &&
info.nr_jited_func_lens &&
info.btf_id) {
jited_linfo_cnt = info.jited_line_info_cnt;
jited_linfo = malloc(jited_linfo_cnt * jited_linfo_rec_size);
if (!jited_linfo) {
p_err("mem alloc failed");
close(fd);
goto err_free;
}
}
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
*member_ptr = ptr_to_u64(buf); *member_ptr = ptr_to_u64(buf);
...@@ -554,6 +585,13 @@ static int do_dump(int argc, char **argv) ...@@ -554,6 +585,13 @@ static int do_dump(int argc, char **argv)
info.func_info_cnt = finfo_cnt; info.func_info_cnt = finfo_cnt;
info.func_info_rec_size = finfo_rec_size; info.func_info_rec_size = finfo_rec_size;
info.func_info = ptr_to_u64(func_info); info.func_info = ptr_to_u64(func_info);
info.line_info_cnt = linfo_cnt;
info.line_info_rec_size = linfo_rec_size;
info.line_info = ptr_to_u64(linfo);
info.jited_line_info_cnt = jited_linfo_cnt;
info.jited_line_info_rec_size = jited_linfo_rec_size;
info.jited_line_info = ptr_to_u64(jited_linfo);
err = bpf_obj_get_info_by_fd(fd, &info, &len); err = bpf_obj_get_info_by_fd(fd, &info, &len);
close(fd); close(fd);
...@@ -596,6 +634,30 @@ static int do_dump(int argc, char **argv) ...@@ -596,6 +634,30 @@ static int do_dump(int argc, char **argv)
finfo_cnt = 0; finfo_cnt = 0;
} }
if (linfo && info.line_info_cnt != linfo_cnt) {
p_err("incorrect line_info_cnt %u vs. expected %u",
info.line_info_cnt, linfo_cnt);
goto err_free;
}
if (info.line_info_rec_size != linfo_rec_size) {
p_err("incorrect line_info_rec_size %u vs. expected %u",
info.line_info_rec_size, linfo_rec_size);
goto err_free;
}
if (jited_linfo && info.jited_line_info_cnt != jited_linfo_cnt) {
p_err("incorrect jited_line_info_cnt %u vs. expected %u",
info.jited_line_info_cnt, jited_linfo_cnt);
goto err_free;
}
if (info.jited_line_info_rec_size != jited_linfo_rec_size) {
p_err("incorrect jited_line_info_rec_size %u vs. expected %u",
info.jited_line_info_rec_size, jited_linfo_rec_size);
goto err_free;
}
if ((member_len == &info.jited_prog_len && if ((member_len == &info.jited_prog_len &&
info.jited_prog_insns == 0) || info.jited_prog_insns == 0) ||
(member_len == &info.xlated_prog_len && (member_len == &info.xlated_prog_len &&
...@@ -609,6 +671,12 @@ static int do_dump(int argc, char **argv) ...@@ -609,6 +671,12 @@ static int do_dump(int argc, char **argv)
goto err_free; goto err_free;
} }
if (linfo_cnt) {
prog_linfo = bpf_prog_linfo__new(&info);
if (!prog_linfo)
p_err("error in processing bpf_line_info. continue without it.");
}
if (filepath) { if (filepath) {
fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0) { if (fd < 0) {
...@@ -690,8 +758,11 @@ static int do_dump(int argc, char **argv) ...@@ -690,8 +758,11 @@ static int do_dump(int argc, char **argv)
printf("%s:\n", sym_name); printf("%s:\n", sym_name);
} }
disasm_print_insn(img, lens[i], opcodes, name, disasm_print_insn(img, lens[i], opcodes,
disasm_opt); name, disasm_opt, btf,
prog_linfo, ksyms[i], i,
linum);
img += lens[i]; img += lens[i];
if (json_output) if (json_output)
...@@ -704,7 +775,7 @@ static int do_dump(int argc, char **argv) ...@@ -704,7 +775,7 @@ static int do_dump(int argc, char **argv)
jsonw_end_array(json_wtr); jsonw_end_array(json_wtr);
} else { } else {
disasm_print_insn(buf, *member_len, opcodes, name, disasm_print_insn(buf, *member_len, opcodes, name,
disasm_opt); disasm_opt, btf, NULL, 0, 0, false);
} }
} else if (visual) { } else if (visual) {
if (json_output) if (json_output)
...@@ -718,11 +789,14 @@ static int do_dump(int argc, char **argv) ...@@ -718,11 +789,14 @@ static int do_dump(int argc, char **argv)
dd.btf = btf; dd.btf = btf;
dd.func_info = func_info; dd.func_info = func_info;
dd.finfo_rec_size = finfo_rec_size; dd.finfo_rec_size = finfo_rec_size;
dd.prog_linfo = prog_linfo;
if (json_output) if (json_output)
dump_xlated_json(&dd, buf, *member_len, opcodes); dump_xlated_json(&dd, buf, *member_len, opcodes,
linum);
else else
dump_xlated_plain(&dd, buf, *member_len, opcodes); dump_xlated_plain(&dd, buf, *member_len, opcodes,
linum);
kernel_syms_destroy(&dd); kernel_syms_destroy(&dd);
} }
...@@ -730,6 +804,9 @@ static int do_dump(int argc, char **argv) ...@@ -730,6 +804,9 @@ static int do_dump(int argc, char **argv)
free(func_ksyms); free(func_ksyms);
free(func_lens); free(func_lens);
free(func_info); free(func_info);
free(linfo);
free(jited_linfo);
bpf_prog_linfo__free(prog_linfo);
return 0; return 0;
err_free: err_free:
...@@ -737,6 +814,9 @@ static int do_dump(int argc, char **argv) ...@@ -737,6 +814,9 @@ static int do_dump(int argc, char **argv)
free(func_ksyms); free(func_ksyms);
free(func_lens); free(func_lens);
free(func_info); free(func_info);
free(linfo);
free(jited_linfo);
bpf_prog_linfo__free(prog_linfo);
return -1; return -1;
} }
...@@ -1138,8 +1218,8 @@ static int do_help(int argc, char **argv) ...@@ -1138,8 +1218,8 @@ static int do_help(int argc, char **argv)
fprintf(stderr, fprintf(stderr,
"Usage: %s %s { show | list } [PROG]\n" "Usage: %s %s { show | list } [PROG]\n"
" %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" " %s %s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
" %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s dump jited PROG [{ file FILE | opcodes | linum }]\n"
" %s %s pin PROG FILE\n" " %s %s pin PROG FILE\n"
" %s %s { load | loadall } OBJ PATH \\\n" " %s %s { load | loadall } OBJ PATH \\\n"
" [type TYPE] [dev NAME] \\\n" " [type TYPE] [dev NAME] \\\n"
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <libbpf.h>
#include "disasm.h" #include "disasm.h"
#include "json_writer.h" #include "json_writer.h"
...@@ -234,8 +235,9 @@ static const char *print_imm(void *private_data, ...@@ -234,8 +235,9 @@ static const char *print_imm(void *private_data,
} }
void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes) bool opcodes, bool linum)
{ {
const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
const struct bpf_insn_cbs cbs = { const struct bpf_insn_cbs cbs = {
.cb_print = print_insn_json, .cb_print = print_insn_json,
.cb_call = print_call, .cb_call = print_call,
...@@ -246,6 +248,7 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, ...@@ -246,6 +248,7 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
struct bpf_insn *insn = buf; struct bpf_insn *insn = buf;
struct btf *btf = dd->btf; struct btf *btf = dd->btf;
bool double_insn = false; bool double_insn = false;
unsigned int nr_skip = 0;
char func_sig[1024]; char func_sig[1024];
unsigned int i; unsigned int i;
...@@ -273,6 +276,16 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, ...@@ -273,6 +276,16 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
} }
} }
if (prog_linfo) {
const struct bpf_line_info *linfo;
linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
if (linfo) {
btf_dump_linfo_json(btf, linfo, linum);
nr_skip++;
}
}
jsonw_name(json_wtr, "disasm"); jsonw_name(json_wtr, "disasm");
print_bpf_insn(&cbs, insn + i, true); print_bpf_insn(&cbs, insn + i, true);
...@@ -307,8 +320,9 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, ...@@ -307,8 +320,9 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
} }
void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes) bool opcodes, bool linum)
{ {
const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
const struct bpf_insn_cbs cbs = { const struct bpf_insn_cbs cbs = {
.cb_print = print_insn, .cb_print = print_insn,
.cb_call = print_call, .cb_call = print_call,
...@@ -318,6 +332,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, ...@@ -318,6 +332,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
struct bpf_func_info *record; struct bpf_func_info *record;
struct bpf_insn *insn = buf; struct bpf_insn *insn = buf;
struct btf *btf = dd->btf; struct btf *btf = dd->btf;
unsigned int nr_skip = 0;
bool double_insn = false; bool double_insn = false;
char func_sig[1024]; char func_sig[1024];
unsigned int i; unsigned int i;
...@@ -340,6 +355,17 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, ...@@ -340,6 +355,17 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
} }
} }
if (prog_linfo) {
const struct bpf_line_info *linfo;
linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
if (linfo) {
btf_dump_linfo_plain(btf, linfo, "; ",
linum);
nr_skip++;
}
}
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
printf("% 4d: ", i); printf("% 4d: ", i);
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#define SYM_MAX_NAME 256 #define SYM_MAX_NAME 256
struct bpf_prog_linfo;
struct kernel_sym { struct kernel_sym {
unsigned long address; unsigned long address;
char name[SYM_MAX_NAME]; char name[SYM_MAX_NAME];
...@@ -54,6 +56,7 @@ struct dump_data { ...@@ -54,6 +56,7 @@ struct dump_data {
struct btf *btf; struct btf *btf;
void *func_info; void *func_info;
__u32 finfo_rec_size; __u32 finfo_rec_size;
const struct bpf_prog_linfo *prog_linfo;
char scratch_buff[SYM_MAX_NAME + 8]; char scratch_buff[SYM_MAX_NAME + 8];
}; };
...@@ -61,9 +64,9 @@ void kernel_syms_load(struct dump_data *dd); ...@@ -61,9 +64,9 @@ void kernel_syms_load(struct dump_data *dd);
void kernel_syms_destroy(struct dump_data *dd); void kernel_syms_destroy(struct dump_data *dd);
struct kernel_sym *kernel_syms_search(struct dump_data *dd, unsigned long key); struct kernel_sym *kernel_syms_search(struct dump_data *dd, unsigned long key);
void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes); bool opcodes, bool linum);
void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes); bool opcodes, bool linum);
void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end, void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end,
unsigned int start_index); unsigned int start_index);
......
...@@ -356,6 +356,9 @@ union bpf_attr { ...@@ -356,6 +356,9 @@ union bpf_attr {
__u32 func_info_rec_size; /* userspace bpf_func_info size */ __u32 func_info_rec_size; /* userspace bpf_func_info size */
__aligned_u64 func_info; /* func info */ __aligned_u64 func_info; /* func info */
__u32 func_info_cnt; /* number of bpf_func_info records */ __u32 func_info_cnt; /* number of bpf_func_info records */
__u32 line_info_rec_size; /* userspace bpf_line_info size */
__aligned_u64 line_info; /* line info */
__u32 line_info_cnt; /* number of bpf_line_info records */
}; };
struct { /* anonymous struct used by BPF_OBJ_* commands */ struct { /* anonymous struct used by BPF_OBJ_* commands */
...@@ -2679,6 +2682,12 @@ struct bpf_prog_info { ...@@ -2679,6 +2682,12 @@ struct bpf_prog_info {
__u32 func_info_rec_size; __u32 func_info_rec_size;
__aligned_u64 func_info; __aligned_u64 func_info;
__u32 func_info_cnt; __u32 func_info_cnt;
__u32 line_info_cnt;
__aligned_u64 line_info;
__aligned_u64 jited_line_info;
__u32 jited_line_info_cnt;
__u32 line_info_rec_size;
__u32 jited_line_info_rec_size;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_map_info { struct bpf_map_info {
...@@ -2995,4 +3004,14 @@ struct bpf_func_info { ...@@ -2995,4 +3004,14 @@ struct bpf_func_info {
__u32 type_id; __u32 type_id;
}; };
#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10)
#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff)
struct bpf_line_info {
__u32 insn_off;
__u32 file_name_off;
__u32 line_off;
__u32 line_col;
};
#endif /* _UAPI__LINUX_BPF_H__ */ #endif /* _UAPI__LINUX_BPF_H__ */
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
...@@ -173,11 +173,36 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name, ...@@ -173,11 +173,36 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
-1); -1);
} }
static void *
alloc_zero_tailing_info(const void *orecord, __u32 cnt,
__u32 actual_rec_size, __u32 expected_rec_size)
{
__u64 info_len = actual_rec_size * cnt;
void *info, *nrecord;
int i;
info = malloc(info_len);
if (!info)
return NULL;
/* zero out bytes kernel does not understand */
nrecord = info;
for (i = 0; i < cnt; i++) {
memcpy(nrecord, orecord, expected_rec_size);
memset(nrecord + expected_rec_size, 0,
actual_rec_size - expected_rec_size);
orecord += actual_rec_size;
nrecord += actual_rec_size;
}
return info;
}
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
char *log_buf, size_t log_buf_sz) char *log_buf, size_t log_buf_sz)
{ {
void *finfo = NULL, *linfo = NULL;
union bpf_attr attr; union bpf_attr attr;
void *finfo = NULL;
__u32 name_len; __u32 name_len;
int fd; int fd;
...@@ -201,53 +226,58 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, ...@@ -201,53 +226,58 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
attr.func_info_rec_size = load_attr->func_info_rec_size; attr.func_info_rec_size = load_attr->func_info_rec_size;
attr.func_info_cnt = load_attr->func_info_cnt; attr.func_info_cnt = load_attr->func_info_cnt;
attr.func_info = ptr_to_u64(load_attr->func_info); attr.func_info = ptr_to_u64(load_attr->func_info);
attr.line_info_rec_size = load_attr->line_info_rec_size;
attr.line_info_cnt = load_attr->line_info_cnt;
attr.line_info = ptr_to_u64(load_attr->line_info);
memcpy(attr.prog_name, load_attr->name, memcpy(attr.prog_name, load_attr->name,
min(name_len, BPF_OBJ_NAME_LEN - 1)); min(name_len, BPF_OBJ_NAME_LEN - 1));
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (fd >= 0 || !log_buf || !log_buf_sz) if (fd >= 0)
return fd; return fd;
/* After bpf_prog_load, the kernel may modify certain attributes /* After bpf_prog_load, the kernel may modify certain attributes
* to give user space a hint how to deal with loading failure. * to give user space a hint how to deal with loading failure.
* Check to see whether we can make some changes and load again. * Check to see whether we can make some changes and load again.
*/ */
if (errno == E2BIG && attr.func_info_cnt && while (errno == E2BIG && (!finfo || !linfo)) {
if (!finfo && attr.func_info_cnt &&
attr.func_info_rec_size < load_attr->func_info_rec_size) { attr.func_info_rec_size < load_attr->func_info_rec_size) {
__u32 actual_rec_size = load_attr->func_info_rec_size; /* try with corrected func info records */
__u32 expected_rec_size = attr.func_info_rec_size; finfo = alloc_zero_tailing_info(load_attr->func_info,
__u32 finfo_cnt = load_attr->func_info_cnt; load_attr->func_info_cnt,
__u64 finfo_len = actual_rec_size * finfo_cnt; load_attr->func_info_rec_size,
const void *orecord; attr.func_info_rec_size);
void *nrecord;
int i;
finfo = malloc(finfo_len);
if (!finfo) if (!finfo)
/* further try with log buffer won't help */ goto done;
return fd;
/* zero out bytes kernel does not understand */
orecord = load_attr->func_info;
nrecord = finfo;
for (i = 0; i < load_attr->func_info_cnt; i++) {
memcpy(nrecord, orecord, expected_rec_size);
memset(nrecord + expected_rec_size, 0,
actual_rec_size - expected_rec_size);
orecord += actual_rec_size;
nrecord += actual_rec_size;
}
/* try with corrected func info records */
attr.func_info = ptr_to_u64(finfo); attr.func_info = ptr_to_u64(finfo);
attr.func_info_rec_size = load_attr->func_info_rec_size; attr.func_info_rec_size = load_attr->func_info_rec_size;
} else if (!linfo && attr.line_info_cnt &&
attr.line_info_rec_size <
load_attr->line_info_rec_size) {
linfo = alloc_zero_tailing_info(load_attr->line_info,
load_attr->line_info_cnt,
load_attr->line_info_rec_size,
attr.line_info_rec_size);
if (!linfo)
goto done;
attr.line_info = ptr_to_u64(linfo);
attr.line_info_rec_size = load_attr->line_info_rec_size;
} else {
break;
}
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (fd >= 0 || !log_buf || !log_buf_sz) if (fd >= 0)
goto done; goto done;
} }
if (!log_buf || !log_buf_sz)
goto done;
/* Try again with log */ /* Try again with log */
attr.log_buf = ptr_to_u64(log_buf); attr.log_buf = ptr_to_u64(log_buf);
attr.log_size = log_buf_sz; attr.log_size = log_buf_sz;
...@@ -256,6 +286,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, ...@@ -256,6 +286,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
done: done:
free(finfo); free(finfo);
free(linfo);
return fd; return fd;
} }
......
...@@ -82,6 +82,9 @@ struct bpf_load_program_attr { ...@@ -82,6 +82,9 @@ struct bpf_load_program_attr {
__u32 func_info_rec_size; __u32 func_info_rec_size;
const void *func_info; const void *func_info;
__u32 func_info_cnt; __u32 func_info_cnt;
__u32 line_info_rec_size;
const void *line_info;
__u32 line_info_cnt;
}; };
/* Flags to direct loading requirements */ /* Flags to direct loading requirements */
......
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2018 Facebook */
#include <string.h>
#include <stdlib.h>
#include <linux/err.h>
#include <linux/bpf.h>
#include "libbpf.h"
#ifndef min
#define min(x, y) ((x) < (y) ? (x) : (y))
#endif
struct bpf_prog_linfo {
void *raw_linfo;
void *raw_jited_linfo;
__u32 *nr_jited_linfo_per_func;
__u32 *jited_linfo_func_idx;
__u32 nr_linfo;
__u32 nr_jited_func;
__u32 rec_size;
__u32 jited_rec_size;
};
static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
const __u64 *ksym_func, const __u32 *ksym_len)
{
__u32 nr_jited_func, nr_linfo;
const void *raw_jited_linfo;
const __u64 *jited_linfo;
__u64 last_jited_linfo;
/*
* Index to raw_jited_linfo:
* i: Index for searching the next ksym_func
* prev_i: Index to the last found ksym_func
*/
__u32 i, prev_i;
__u32 f; /* Index to ksym_func */
raw_jited_linfo = prog_linfo->raw_jited_linfo;
jited_linfo = raw_jited_linfo;
if (ksym_func[0] != *jited_linfo)
goto errout;
prog_linfo->jited_linfo_func_idx[0] = 0;
nr_jited_func = prog_linfo->nr_jited_func;
nr_linfo = prog_linfo->nr_linfo;
for (prev_i = 0, i = 1, f = 1;
i < nr_linfo && f < nr_jited_func;
i++) {
raw_jited_linfo += prog_linfo->jited_rec_size;
last_jited_linfo = *jited_linfo;
jited_linfo = raw_jited_linfo;
if (ksym_func[f] == *jited_linfo) {
prog_linfo->jited_linfo_func_idx[f] = i;
/* Sanity check */
if (last_jited_linfo - ksym_func[f - 1] + 1 >
ksym_len[f - 1])
goto errout;
prog_linfo->nr_jited_linfo_per_func[f - 1] =
i - prev_i;
prev_i = i;
/*
* The ksym_func[f] is found in jited_linfo.
* Look for the next one.
*/
f++;
} else if (*jited_linfo <= last_jited_linfo) {
/* Ensure the addr is increasing _within_ a func */
goto errout;
}
}
if (f != nr_jited_func)
goto errout;
prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
nr_linfo - prev_i;
return 0;
errout:
return -EINVAL;
}
void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
{
if (!prog_linfo)
return;
free(prog_linfo->raw_linfo);
free(prog_linfo->raw_jited_linfo);
free(prog_linfo->nr_jited_linfo_per_func);
free(prog_linfo->jited_linfo_func_idx);
free(prog_linfo);
}
struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
{
struct bpf_prog_linfo *prog_linfo;
__u32 nr_linfo, nr_jited_func;
nr_linfo = info->line_info_cnt;
/*
* Test !info->line_info because the kernel may NULL
* the ptr if kernel.kptr_restrict is set.
*/
if (!nr_linfo || !info->line_info)
return NULL;
/*
* The min size that bpf_prog_linfo has to access for
* searching purpose.
*/
if (info->line_info_rec_size <
offsetof(struct bpf_line_info, file_name_off))
return NULL;
prog_linfo = calloc(1, sizeof(*prog_linfo));
if (!prog_linfo)
return NULL;
/* Copy xlated line_info */
prog_linfo->nr_linfo = nr_linfo;
prog_linfo->rec_size = info->line_info_rec_size;
prog_linfo->raw_linfo = malloc(nr_linfo * prog_linfo->rec_size);
if (!prog_linfo->raw_linfo)
goto err_free;
memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info,
nr_linfo * prog_linfo->rec_size);
nr_jited_func = info->nr_jited_ksyms;
if (!nr_jited_func ||
!info->jited_line_info ||
info->jited_line_info_cnt != nr_linfo ||
info->jited_line_info_rec_size < sizeof(__u64) ||
info->nr_jited_func_lens != nr_jited_func ||
!info->jited_ksyms ||
!info->jited_func_lens)
/* Not enough info to provide jited_line_info */
return prog_linfo;
/* Copy jited_line_info */
prog_linfo->nr_jited_func = nr_jited_func;
prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
prog_linfo->raw_jited_linfo = malloc(nr_linfo *
prog_linfo->jited_rec_size);
if (!prog_linfo->raw_jited_linfo)
goto err_free;
memcpy(prog_linfo->raw_jited_linfo,
(void *)(long)info->jited_line_info,
nr_linfo * prog_linfo->jited_rec_size);
/* Number of jited_line_info per jited func */
prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
sizeof(__u32));
if (!prog_linfo->nr_jited_linfo_per_func)
goto err_free;
/*
* For each jited func,
* the start idx to the "linfo" and "jited_linfo" array,
*/
prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
sizeof(__u32));
if (!prog_linfo->jited_linfo_func_idx)
goto err_free;
if (dissect_jited_func(prog_linfo,
(__u64 *)(long)info->jited_ksyms,
(__u32 *)(long)info->jited_func_lens))
goto err_free;
return prog_linfo;
err_free:
bpf_prog_linfo__free(prog_linfo);
return NULL;
}
const struct bpf_line_info *
bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
__u64 addr, __u32 func_idx, __u32 nr_skip)
{
__u32 jited_rec_size, rec_size, nr_linfo, start, i;
const void *raw_jited_linfo, *raw_linfo;
const __u64 *jited_linfo;
if (func_idx >= prog_linfo->nr_jited_func)
return NULL;
nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
if (nr_skip >= nr_linfo)
return NULL;
start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
jited_rec_size = prog_linfo->jited_rec_size;
raw_jited_linfo = prog_linfo->raw_jited_linfo +
(start * jited_rec_size);
jited_linfo = raw_jited_linfo;
if (addr < *jited_linfo)
return NULL;
nr_linfo -= nr_skip;
rec_size = prog_linfo->rec_size;
raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
for (i = 0; i < nr_linfo; i++) {
if (addr < *jited_linfo)
break;
raw_linfo += rec_size;
raw_jited_linfo += jited_rec_size;
jited_linfo = raw_jited_linfo;
}
return raw_linfo - rec_size;
}
const struct bpf_line_info *
bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
__u32 insn_off, __u32 nr_skip)
{
const struct bpf_line_info *linfo;
__u32 rec_size, nr_linfo, i;
const void *raw_linfo;
nr_linfo = prog_linfo->nr_linfo;
if (nr_skip >= nr_linfo)
return NULL;
rec_size = prog_linfo->rec_size;
raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
linfo = raw_linfo;
if (insn_off < linfo->insn_off)
return NULL;
nr_linfo -= nr_skip;
for (i = 0; i < nr_linfo; i++) {
if (insn_off < linfo->insn_off)
break;
raw_linfo += rec_size;
linfo = raw_linfo;
}
return raw_linfo - rec_size;
}
This diff is collapsed.
...@@ -51,13 +51,8 @@ struct btf_ext_header { ...@@ -51,13 +51,8 @@ struct btf_ext_header {
/* All offsets are in bytes relative to the end of this header */ /* All offsets are in bytes relative to the end of this header */
__u32 func_info_off; __u32 func_info_off;
__u32 func_info_len; __u32 func_info_len;
}; __u32 line_info_off;
__u32 line_info_len;
struct btf_sec_func_info {
__u32 sec_name_off;
__u32 num_func_info;
/* Followed by num_func_info number of bpf func_info records */
__u8 data[0];
}; };
typedef int (*btf_print_fn_t)(const char *, ...) typedef int (*btf_print_fn_t)(const char *, ...)
...@@ -77,12 +72,16 @@ LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf); ...@@ -77,12 +72,16 @@ LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log); struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
void btf_ext__free(struct btf_ext *btf_ext); void btf_ext__free(struct btf_ext *btf_ext);
int btf_ext__reloc_init(struct btf *btf, struct btf_ext *btf_ext, int btf_ext__reloc_func_info(const struct btf *btf,
const char *sec_name, void **func_info, const struct btf_ext *btf_ext,
__u32 *func_info_rec_size, __u32 *func_info_len); const char *sec_name, __u32 insns_cnt,
int btf_ext__reloc(struct btf *btf, struct btf_ext *btf_ext, void **func_info, __u32 *func_info_len);
const char *sec_name, __u32 insns_cnt, void **func_info, int btf_ext__reloc_line_info(const struct btf *btf,
__u32 *func_info_len); const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **line_info, __u32 *cnt);
__u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
__u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
......
...@@ -167,9 +167,13 @@ struct bpf_program { ...@@ -167,9 +167,13 @@ struct bpf_program {
int btf_fd; int btf_fd;
void *func_info; void *func_info;
__u32 func_info_rec_size; __u32 func_info_rec_size;
__u32 func_info_len; __u32 func_info_cnt;
struct bpf_capabilities *caps; struct bpf_capabilities *caps;
void *line_info;
__u32 line_info_rec_size;
__u32 line_info_cnt;
}; };
struct bpf_map { struct bpf_map {
...@@ -779,6 +783,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags) ...@@ -779,6 +783,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
{ {
Elf *elf = obj->efile.elf; Elf *elf = obj->efile.elf;
GElf_Ehdr *ep = &obj->efile.ehdr; GElf_Ehdr *ep = &obj->efile.ehdr;
Elf_Data *btf_ext_data = NULL;
Elf_Scn *scn = NULL; Elf_Scn *scn = NULL;
int idx = 0, err = 0; int idx = 0, err = 0;
...@@ -841,14 +846,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags) ...@@ -841,14 +846,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
obj->btf = NULL; obj->btf = NULL;
} }
} else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) { } else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
obj->btf_ext = btf_ext__new(data->d_buf, data->d_size, btf_ext_data = data;
__pr_debug);
if (IS_ERR(obj->btf_ext)) {
pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
BTF_EXT_ELF_SEC,
PTR_ERR(obj->btf_ext));
obj->btf_ext = NULL;
}
} else if (sh.sh_type == SHT_SYMTAB) { } else if (sh.sh_type == SHT_SYMTAB) {
if (obj->efile.symbols) { if (obj->efile.symbols) {
pr_warning("bpf: multiple SYMTAB in %s\n", pr_warning("bpf: multiple SYMTAB in %s\n",
...@@ -910,6 +908,22 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags) ...@@ -910,6 +908,22 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
pr_warning("Corrupted ELF file: index of strtab invalid\n"); pr_warning("Corrupted ELF file: index of strtab invalid\n");
return LIBBPF_ERRNO__FORMAT; return LIBBPF_ERRNO__FORMAT;
} }
if (btf_ext_data) {
if (!obj->btf) {
pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
BTF_EXT_ELF_SEC, BTF_ELF_SEC);
} else {
obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
btf_ext_data->d_size,
__pr_debug);
if (IS_ERR(obj->btf_ext)) {
pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
BTF_EXT_ELF_SEC,
PTR_ERR(obj->btf_ext));
obj->btf_ext = NULL;
}
}
}
if (obj->efile.maps_shndx >= 0) { if (obj->efile.maps_shndx >= 0) {
err = bpf_object__init_maps(obj, flags); err = bpf_object__init_maps(obj, flags);
if (err) if (err)
...@@ -1275,6 +1289,82 @@ bpf_object__create_maps(struct bpf_object *obj) ...@@ -1275,6 +1289,82 @@ bpf_object__create_maps(struct bpf_object *obj)
return 0; return 0;
} }
static int
check_btf_ext_reloc_err(struct bpf_program *prog, int err,
void *btf_prog_info, const char *info_name)
{
if (err != -ENOENT) {
pr_warning("Error in loading %s for sec %s.\n",
info_name, prog->section_name);
return err;
}
/* err == -ENOENT (i.e. prog->section_name not found in btf_ext) */
if (btf_prog_info) {
/*
* Some info has already been found but has problem
* in the last btf_ext reloc. Must have to error
* out.
*/
pr_warning("Error in relocating %s for sec %s.\n",
info_name, prog->section_name);
return err;
}
/*
* Have problem loading the very first info. Ignore
* the rest.
*/
pr_warning("Cannot find %s for main program sec %s. Ignore all %s.\n",
info_name, prog->section_name, info_name);
return 0;
}
static int
bpf_program_reloc_btf_ext(struct bpf_program *prog, struct bpf_object *obj,
const char *section_name, __u32 insn_offset)
{
int err;
if (!insn_offset || prog->func_info) {
/*
* !insn_offset => main program
*
* For sub prog, the main program's func_info has to
* be loaded first (i.e. prog->func_info != NULL)
*/
err = btf_ext__reloc_func_info(obj->btf, obj->btf_ext,
section_name, insn_offset,
&prog->func_info,
&prog->func_info_cnt);
if (err)
return check_btf_ext_reloc_err(prog, err,
prog->func_info,
"bpf_func_info");
prog->func_info_rec_size = btf_ext__func_info_rec_size(obj->btf_ext);
}
if (!insn_offset || prog->line_info) {
err = btf_ext__reloc_line_info(obj->btf, obj->btf_ext,
section_name, insn_offset,
&prog->line_info,
&prog->line_info_cnt);
if (err)
return check_btf_ext_reloc_err(prog, err,
prog->line_info,
"bpf_line_info");
prog->line_info_rec_size = btf_ext__line_info_rec_size(obj->btf_ext);
}
if (!insn_offset)
prog->btf_fd = btf__fd(obj->btf);
return 0;
}
static int static int
bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj, bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
struct reloc_desc *relo) struct reloc_desc *relo)
...@@ -1306,18 +1396,13 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj, ...@@ -1306,18 +1396,13 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
return -ENOMEM; return -ENOMEM;
} }
if (obj->btf && obj->btf_ext) { if (obj->btf_ext) {
err = btf_ext__reloc(obj->btf, obj->btf_ext, err = bpf_program_reloc_btf_ext(prog, obj,
text->section_name, text->section_name,
prog->insns_cnt, prog->insns_cnt);
&prog->func_info, if (err)
&prog->func_info_len);
if (err) {
pr_warning("error in btf_ext__reloc for sec %s\n",
text->section_name);
return err; return err;
} }
}
memcpy(new_insn + prog->insns_cnt, text->insns, memcpy(new_insn + prog->insns_cnt, text->insns,
text->insns_cnt * sizeof(*insn)); text->insns_cnt * sizeof(*insn));
...@@ -1341,19 +1426,12 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) ...@@ -1341,19 +1426,12 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
if (!prog) if (!prog)
return 0; return 0;
if (obj->btf && obj->btf_ext) { if (obj->btf_ext) {
err = btf_ext__reloc_init(obj->btf, obj->btf_ext, err = bpf_program_reloc_btf_ext(prog, obj,
prog->section_name, prog->section_name, 0);
&prog->func_info, if (err)
&prog->func_info_rec_size,
&prog->func_info_len);
if (err) {
pr_warning("err in btf_ext__reloc_init for sec %s\n",
prog->section_name);
return err; return err;
} }
prog->btf_fd = btf__fd(obj->btf);
}
if (!prog->reloc_desc) if (!prog->reloc_desc)
return 0; return 0;
...@@ -1444,8 +1522,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) ...@@ -1444,8 +1522,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
static int static int
load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
char *license, __u32 kern_version, int *pfd, char *license, __u32 kern_version, int *pfd)
__u32 func_info_cnt)
{ {
struct bpf_load_program_attr load_attr; struct bpf_load_program_attr load_attr;
char *cp, errmsg[STRERR_BUFSIZE]; char *cp, errmsg[STRERR_BUFSIZE];
...@@ -1465,8 +1542,10 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, ...@@ -1465,8 +1542,10 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.prog_btf_fd = prog->btf_fd >= 0 ? prog->btf_fd : 0; load_attr.prog_btf_fd = prog->btf_fd >= 0 ? prog->btf_fd : 0;
load_attr.func_info = prog->func_info; load_attr.func_info = prog->func_info;
load_attr.func_info_rec_size = prog->func_info_rec_size; load_attr.func_info_rec_size = prog->func_info_rec_size;
load_attr.func_info_cnt = func_info_cnt; load_attr.func_info_cnt = prog->func_info_cnt;
load_attr.line_info = prog->line_info;
load_attr.line_info_rec_size = prog->line_info_rec_size;
load_attr.line_info_cnt = prog->line_info_cnt;
if (!load_attr.insns || !load_attr.insns_cnt) if (!load_attr.insns || !load_attr.insns_cnt)
return -EINVAL; return -EINVAL;
...@@ -1523,14 +1602,8 @@ int ...@@ -1523,14 +1602,8 @@ int
bpf_program__load(struct bpf_program *prog, bpf_program__load(struct bpf_program *prog,
char *license, __u32 kern_version) char *license, __u32 kern_version)
{ {
__u32 func_info_cnt;
int err = 0, fd, i; int err = 0, fd, i;
if (prog->func_info_len == 0)
func_info_cnt = 0;
else
func_info_cnt = prog->func_info_len / prog->func_info_rec_size;
if (prog->instances.nr < 0 || !prog->instances.fds) { if (prog->instances.nr < 0 || !prog->instances.fds) {
if (prog->preprocessor) { if (prog->preprocessor) {
pr_warning("Internal error: can't load program '%s'\n", pr_warning("Internal error: can't load program '%s'\n",
...@@ -1553,8 +1626,7 @@ bpf_program__load(struct bpf_program *prog, ...@@ -1553,8 +1626,7 @@ bpf_program__load(struct bpf_program *prog,
prog->section_name, prog->instances.nr); prog->section_name, prog->instances.nr);
} }
err = load_program(prog, prog->insns, prog->insns_cnt, err = load_program(prog, prog->insns, prog->insns_cnt,
license, kern_version, &fd, license, kern_version, &fd);
func_info_cnt);
if (!err) if (!err)
prog->instances.fds[0] = fd; prog->instances.fds[0] = fd;
goto out; goto out;
...@@ -1584,8 +1656,7 @@ bpf_program__load(struct bpf_program *prog, ...@@ -1584,8 +1656,7 @@ bpf_program__load(struct bpf_program *prog,
err = load_program(prog, result.new_insn_ptr, err = load_program(prog, result.new_insn_ptr,
result.new_insn_cnt, result.new_insn_cnt,
license, kern_version, &fd, license, kern_version, &fd);
func_info_cnt);
if (err) { if (err) {
pr_warning("Loading the %dth instance of program '%s' failed\n", pr_warning("Loading the %dth instance of program '%s' failed\n",
......
...@@ -342,6 +342,19 @@ int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex, ...@@ -342,6 +342,19 @@ int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle, int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie); libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
struct bpf_prog_linfo;
struct bpf_prog_info;
LIBBPF_API void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo);
LIBBPF_API struct bpf_prog_linfo *
bpf_prog_linfo__new(const struct bpf_prog_info *info);
LIBBPF_API const struct bpf_line_info *
bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
__u64 addr, __u32 func_idx, __u32 nr_skip);
LIBBPF_API const struct bpf_line_info *
bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
__u32 insn_off, __u32 nr_skip);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif
......
...@@ -99,6 +99,10 @@ LIBBPF_0.0.1 { ...@@ -99,6 +99,10 @@ LIBBPF_0.0.1 {
bpf_program__unload; bpf_program__unload;
bpf_program__unpin; bpf_program__unpin;
bpf_program__unpin_instance; bpf_program__unpin_instance;
bpf_prog_linfo__free;
bpf_prog_linfo__new;
bpf_prog_linfo__lfind_addr_func;
bpf_prog_linfo__lfind;
bpf_raw_tracepoint_open; bpf_raw_tracepoint_open;
bpf_set_link_xdp_fd; bpf_set_link_xdp_fd;
bpf_task_fd_query; bpf_task_fd_query;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment