1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2018 Netronome Systems, Inc. */
10 #include <bpf/libbpf.h>
13 #include "json_writer.h"
15 #include "xlated_dumper.h"
17 static int kernel_syms_cmp(const void *sym_a
, const void *sym_b
)
19 return ((struct kernel_sym
*)sym_a
)->address
-
20 ((struct kernel_sym
*)sym_b
)->address
;
23 void kernel_syms_load(struct dump_data
*dd
)
25 struct kernel_sym
*sym
;
30 fp
= fopen("/proc/kallsyms", "r");
34 while (fgets(buff
, sizeof(buff
), fp
)) {
35 tmp
= reallocarray(dd
->sym_mapping
, dd
->sym_count
+ 1,
36 sizeof(*dd
->sym_mapping
));
39 free(dd
->sym_mapping
);
40 dd
->sym_mapping
= NULL
;
44 dd
->sym_mapping
= tmp
;
45 sym
= &dd
->sym_mapping
[dd
->sym_count
];
46 if (sscanf(buff
, "%p %*c %s", &address
, sym
->name
) != 2)
48 sym
->address
= (unsigned long)address
;
49 if (!strcmp(sym
->name
, "__bpf_call_base")) {
50 dd
->address_call_base
= sym
->address
;
51 /* sysctl kernel.kptr_restrict was set */
61 qsort(dd
->sym_mapping
, dd
->sym_count
,
62 sizeof(*dd
->sym_mapping
), kernel_syms_cmp
);
65 void kernel_syms_destroy(struct dump_data
*dd
)
67 free(dd
->sym_mapping
);
70 struct kernel_sym
*kernel_syms_search(struct dump_data
*dd
,
73 struct kernel_sym sym
= {
77 return dd
->sym_mapping
?
78 bsearch(&sym
, dd
->sym_mapping
, dd
->sym_count
,
79 sizeof(*dd
->sym_mapping
), kernel_syms_cmp
) : NULL
;
82 static void __printf(2, 3) print_insn(void *private_data
, const char *fmt
, ...)
91 static void __printf(2, 3)
92 print_insn_for_graph(void *private_data
, const char *fmt
, ...)
98 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
104 memmove(p
+ 3, p
, strlen(buf
) + 1 - (p
- buf
));
105 /* Align each instruction dump row left. */
108 /* Output multiline concatenation. */
110 } else if (*p
== '<' || *p
== '>' || *p
== '|' || *p
== '&') {
111 memmove(p
+ 1, p
, strlen(buf
) + 1 - (p
- buf
));
112 /* Escape special character. */
122 static void __printf(2, 3)
123 print_insn_json(void *private_data
, const char *fmt
, ...)
125 unsigned int l
= strlen(fmt
);
131 strncpy(chomped_fmt
, fmt
, l
- 1);
132 chomped_fmt
[l
- 1] = '\0';
134 jsonw_vprintf_enquote(json_wtr
, chomped_fmt
, args
);
138 static const char *print_call_pcrel(struct dump_data
*dd
,
139 struct kernel_sym
*sym
,
140 unsigned long address
,
141 const struct bpf_insn
*insn
)
143 if (!dd
->nr_jited_ksyms
)
144 /* Do not show address for interpreted programs */
145 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
148 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
149 "%+d#%s", insn
->off
, sym
->name
);
151 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
152 "%+d#0x%lx", insn
->off
, address
);
153 return dd
->scratch_buff
;
156 static const char *print_call_helper(struct dump_data
*dd
,
157 struct kernel_sym
*sym
,
158 unsigned long address
)
161 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
164 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
166 return dd
->scratch_buff
;
169 static const char *print_call(void *private_data
,
170 const struct bpf_insn
*insn
)
172 struct dump_data
*dd
= private_data
;
173 unsigned long address
= dd
->address_call_base
+ insn
->imm
;
174 struct kernel_sym
*sym
;
176 if (insn
->src_reg
== BPF_PSEUDO_CALL
&&
177 (__u32
) insn
->imm
< dd
->nr_jited_ksyms
&& dd
->jited_ksyms
)
178 address
= dd
->jited_ksyms
[insn
->imm
];
180 sym
= kernel_syms_search(dd
, address
);
181 if (insn
->src_reg
== BPF_PSEUDO_CALL
)
182 return print_call_pcrel(dd
, sym
, address
, insn
);
184 return print_call_helper(dd
, sym
, address
);
187 static const char *print_imm(void *private_data
,
188 const struct bpf_insn
*insn
,
191 struct dump_data
*dd
= private_data
;
193 if (insn
->src_reg
== BPF_PSEUDO_MAP_FD
)
194 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
195 "map[id:%u]", insn
->imm
);
196 else if (insn
->src_reg
== BPF_PSEUDO_MAP_VALUE
)
197 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
198 "map[id:%u][0]+%u", insn
->imm
, (insn
+ 1)->imm
);
200 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
201 "0x%llx", (unsigned long long)full_imm
);
202 return dd
->scratch_buff
;
205 void dump_xlated_json(struct dump_data
*dd
, void *buf
, unsigned int len
,
206 bool opcodes
, bool linum
)
208 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
209 const struct bpf_insn_cbs cbs
= {
210 .cb_print
= print_insn_json
,
211 .cb_call
= print_call
,
215 struct bpf_func_info
*record
;
216 struct bpf_insn
*insn
= buf
;
217 struct btf
*btf
= dd
->btf
;
218 bool double_insn
= false;
219 unsigned int nr_skip
= 0;
223 jsonw_start_array(json_wtr
);
224 record
= dd
->func_info
;
225 for (i
= 0; i
< len
/ sizeof(*insn
); i
++) {
230 double_insn
= insn
[i
].code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
232 jsonw_start_object(json_wtr
);
235 if (record
->insn_off
== i
) {
236 btf_dumper_type_only(btf
, record
->type_id
,
239 if (func_sig
[0] != '\0') {
240 jsonw_name(json_wtr
, "proto");
241 jsonw_string(json_wtr
, func_sig
);
243 record
= (void *)record
+ dd
->finfo_rec_size
;
248 const struct bpf_line_info
*linfo
;
250 linfo
= bpf_prog_linfo__lfind(prog_linfo
, i
, nr_skip
);
252 btf_dump_linfo_json(btf
, linfo
, linum
);
257 jsonw_name(json_wtr
, "disasm");
258 print_bpf_insn(&cbs
, insn
+ i
, true);
261 jsonw_name(json_wtr
, "opcodes");
262 jsonw_start_object(json_wtr
);
264 jsonw_name(json_wtr
, "code");
265 jsonw_printf(json_wtr
, "\"0x%02hhx\"", insn
[i
].code
);
267 jsonw_name(json_wtr
, "src_reg");
268 jsonw_printf(json_wtr
, "\"0x%hhx\"", insn
[i
].src_reg
);
270 jsonw_name(json_wtr
, "dst_reg");
271 jsonw_printf(json_wtr
, "\"0x%hhx\"", insn
[i
].dst_reg
);
273 jsonw_name(json_wtr
, "off");
274 print_hex_data_json((uint8_t *)(&insn
[i
].off
), 2);
276 jsonw_name(json_wtr
, "imm");
277 if (double_insn
&& i
< len
- 1)
278 print_hex_data_json((uint8_t *)(&insn
[i
].imm
),
281 print_hex_data_json((uint8_t *)(&insn
[i
].imm
),
283 jsonw_end_object(json_wtr
);
285 jsonw_end_object(json_wtr
);
287 jsonw_end_array(json_wtr
);
290 void dump_xlated_plain(struct dump_data
*dd
, void *buf
, unsigned int len
,
291 bool opcodes
, bool linum
)
293 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
294 const struct bpf_insn_cbs cbs
= {
295 .cb_print
= print_insn
,
296 .cb_call
= print_call
,
300 struct bpf_func_info
*record
;
301 struct bpf_insn
*insn
= buf
;
302 struct btf
*btf
= dd
->btf
;
303 unsigned int nr_skip
= 0;
304 bool double_insn
= false;
308 record
= dd
->func_info
;
309 for (i
= 0; i
< len
/ sizeof(*insn
); i
++) {
316 if (record
->insn_off
== i
) {
317 btf_dumper_type_only(btf
, record
->type_id
,
320 if (func_sig
[0] != '\0')
321 printf("%s:\n", func_sig
);
322 record
= (void *)record
+ dd
->finfo_rec_size
;
327 const struct bpf_line_info
*linfo
;
329 linfo
= bpf_prog_linfo__lfind(prog_linfo
, i
, nr_skip
);
331 btf_dump_linfo_plain(btf
, linfo
, "; ",
337 double_insn
= insn
[i
].code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
340 print_bpf_insn(&cbs
, insn
+ i
, true);
344 fprint_hex(stdout
, insn
+ i
, 8, " ");
345 if (double_insn
&& i
< len
- 1) {
347 fprint_hex(stdout
, insn
+ i
+ 1, 8, " ");
354 void dump_xlated_for_graph(struct dump_data
*dd
, void *buf_start
, void *buf_end
,
355 unsigned int start_idx
)
357 const struct bpf_insn_cbs cbs
= {
358 .cb_print
= print_insn_for_graph
,
359 .cb_call
= print_call
,
363 struct bpf_insn
*insn_start
= buf_start
;
364 struct bpf_insn
*insn_end
= buf_end
;
365 struct bpf_insn
*cur
= insn_start
;
367 for (; cur
<= insn_end
; cur
++) {
368 printf("% 4d: ", (int)(cur
- insn_start
+ start_idx
));
369 print_bpf_insn(&cbs
, cur
, true);