1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2018 Netronome Systems, Inc. */
11 #include <sys/types.h>
12 #include <bpf/libbpf.h>
13 #include <bpf/libbpf_internal.h>
16 #include "json_writer.h"
18 #include "xlated_dumper.h"
20 static int kernel_syms_cmp(const void *sym_a
, const void *sym_b
)
22 return ((struct kernel_sym
*)sym_a
)->address
-
23 ((struct kernel_sym
*)sym_b
)->address
;
26 void kernel_syms_load(struct dump_data
*dd
)
28 struct kernel_sym
*sym
;
33 fp
= fopen("/proc/kallsyms", "r");
37 while (fgets(buff
, sizeof(buff
), fp
)) {
38 tmp
= libbpf_reallocarray(dd
->sym_mapping
, dd
->sym_count
+ 1,
39 sizeof(*dd
->sym_mapping
));
42 free(dd
->sym_mapping
);
43 dd
->sym_mapping
= NULL
;
47 dd
->sym_mapping
= tmp
;
48 sym
= &dd
->sym_mapping
[dd
->sym_count
];
50 /* module is optional */
51 sym
->module
[0] = '\0';
52 /* trim the square brackets around the module name */
53 if (sscanf(buff
, "%p %*c %s [%[^]]s", &address
, sym
->name
, sym
->module
) < 2)
55 sym
->address
= (unsigned long)address
;
56 if (!strcmp(sym
->name
, "__bpf_call_base")) {
57 dd
->address_call_base
= sym
->address
;
58 /* sysctl kernel.kptr_restrict was set */
68 qsort(dd
->sym_mapping
, dd
->sym_count
,
69 sizeof(*dd
->sym_mapping
), kernel_syms_cmp
);
72 void kernel_syms_destroy(struct dump_data
*dd
)
74 free(dd
->sym_mapping
);
77 struct kernel_sym
*kernel_syms_search(struct dump_data
*dd
,
80 struct kernel_sym sym
= {
84 return dd
->sym_mapping
?
85 bsearch(&sym
, dd
->sym_mapping
, dd
->sym_count
,
86 sizeof(*dd
->sym_mapping
), kernel_syms_cmp
) : NULL
;
89 static void __printf(2, 3) print_insn(void *private_data
, const char *fmt
, ...)
98 static void __printf(2, 3)
99 print_insn_for_graph(void *private_data
, const char *fmt
, ...)
105 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
111 memmove(p
+ 3, p
, strlen(buf
) + 1 - (p
- buf
));
112 /* Align each instruction dump row left. */
115 /* Output multiline concatenation. */
117 } else if (*p
== '<' || *p
== '>' || *p
== '|' || *p
== '&') {
118 memmove(p
+ 1, p
, strlen(buf
) + 1 - (p
- buf
));
119 /* Escape special character. */
129 static void __printf(2, 3)
130 print_insn_json(void *private_data
, const char *fmt
, ...)
132 unsigned int l
= strlen(fmt
);
138 strncpy(chomped_fmt
, fmt
, l
- 1);
139 chomped_fmt
[l
- 1] = '\0';
141 jsonw_vprintf_enquote(json_wtr
, chomped_fmt
, args
);
145 static const char *print_call_pcrel(struct dump_data
*dd
,
146 struct kernel_sym
*sym
,
147 unsigned long address
,
148 const struct bpf_insn
*insn
)
150 if (!dd
->nr_jited_ksyms
)
151 /* Do not show address for interpreted programs */
152 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
155 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
156 "%+d#%s", insn
->off
, sym
->name
);
158 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
159 "%+d#0x%lx", insn
->off
, address
);
160 return dd
->scratch_buff
;
163 static const char *print_call_helper(struct dump_data
*dd
,
164 struct kernel_sym
*sym
,
165 unsigned long address
)
168 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
171 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
173 return dd
->scratch_buff
;
176 static const char *print_call(void *private_data
,
177 const struct bpf_insn
*insn
)
179 struct dump_data
*dd
= private_data
;
180 unsigned long address
= dd
->address_call_base
+ insn
->imm
;
181 struct kernel_sym
*sym
;
183 if (insn
->src_reg
== BPF_PSEUDO_CALL
&&
184 (__u32
) insn
->imm
< dd
->nr_jited_ksyms
&& dd
->jited_ksyms
)
185 address
= dd
->jited_ksyms
[insn
->imm
];
187 sym
= kernel_syms_search(dd
, address
);
188 if (insn
->src_reg
== BPF_PSEUDO_CALL
)
189 return print_call_pcrel(dd
, sym
, address
, insn
);
191 return print_call_helper(dd
, sym
, address
);
194 static const char *print_imm(void *private_data
,
195 const struct bpf_insn
*insn
,
198 struct dump_data
*dd
= private_data
;
200 if (insn
->src_reg
== BPF_PSEUDO_MAP_FD
)
201 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
202 "map[id:%u]", insn
->imm
);
203 else if (insn
->src_reg
== BPF_PSEUDO_MAP_VALUE
)
204 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
205 "map[id:%u][0]+%u", insn
->imm
, (insn
+ 1)->imm
);
206 else if (insn
->src_reg
== BPF_PSEUDO_MAP_IDX_VALUE
)
207 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
208 "map[idx:%u]+%u", insn
->imm
, (insn
+ 1)->imm
);
209 else if (insn
->src_reg
== BPF_PSEUDO_FUNC
)
210 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
211 "subprog[%+d]", insn
->imm
);
213 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
214 "0x%llx", (unsigned long long)full_imm
);
215 return dd
->scratch_buff
;
218 void dump_xlated_json(struct dump_data
*dd
, void *buf
, unsigned int len
,
219 bool opcodes
, bool linum
)
221 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
222 const struct bpf_insn_cbs cbs
= {
223 .cb_print
= print_insn_json
,
224 .cb_call
= print_call
,
228 struct bpf_func_info
*record
;
229 struct bpf_insn
*insn
= buf
;
230 struct btf
*btf
= dd
->btf
;
231 bool double_insn
= false;
232 unsigned int nr_skip
= 0;
236 jsonw_start_array(json_wtr
);
237 record
= dd
->func_info
;
238 for (i
= 0; i
< len
/ sizeof(*insn
); i
++) {
243 double_insn
= insn
[i
].code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
245 jsonw_start_object(json_wtr
);
248 if (record
->insn_off
== i
) {
249 btf_dumper_type_only(btf
, record
->type_id
,
252 if (func_sig
[0] != '\0') {
253 jsonw_name(json_wtr
, "proto");
254 jsonw_string(json_wtr
, func_sig
);
256 record
= (void *)record
+ dd
->finfo_rec_size
;
261 const struct bpf_line_info
*linfo
;
263 linfo
= bpf_prog_linfo__lfind(prog_linfo
, i
, nr_skip
);
265 btf_dump_linfo_json(btf
, linfo
, linum
);
270 jsonw_name(json_wtr
, "disasm");
271 print_bpf_insn(&cbs
, insn
+ i
, true);
274 jsonw_name(json_wtr
, "opcodes");
275 jsonw_start_object(json_wtr
);
277 jsonw_name(json_wtr
, "code");
278 jsonw_printf(json_wtr
, "\"0x%02hhx\"", insn
[i
].code
);
280 jsonw_name(json_wtr
, "src_reg");
281 jsonw_printf(json_wtr
, "\"0x%hhx\"", insn
[i
].src_reg
);
283 jsonw_name(json_wtr
, "dst_reg");
284 jsonw_printf(json_wtr
, "\"0x%hhx\"", insn
[i
].dst_reg
);
286 jsonw_name(json_wtr
, "off");
287 print_hex_data_json((uint8_t *)(&insn
[i
].off
), 2);
289 jsonw_name(json_wtr
, "imm");
290 if (double_insn
&& i
< len
- 1)
291 print_hex_data_json((uint8_t *)(&insn
[i
].imm
),
294 print_hex_data_json((uint8_t *)(&insn
[i
].imm
),
296 jsonw_end_object(json_wtr
);
298 jsonw_end_object(json_wtr
);
300 jsonw_end_array(json_wtr
);
303 void dump_xlated_plain(struct dump_data
*dd
, void *buf
, unsigned int len
,
304 bool opcodes
, bool linum
)
306 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
307 const struct bpf_insn_cbs cbs
= {
308 .cb_print
= print_insn
,
309 .cb_call
= print_call
,
313 struct bpf_func_info
*record
;
314 struct bpf_insn
*insn
= buf
;
315 struct btf
*btf
= dd
->btf
;
316 unsigned int nr_skip
= 0;
317 bool double_insn
= false;
321 record
= dd
->func_info
;
322 for (i
= 0; i
< len
/ sizeof(*insn
); i
++) {
329 if (record
->insn_off
== i
) {
330 btf_dumper_type_only(btf
, record
->type_id
,
333 if (func_sig
[0] != '\0')
334 printf("%s:\n", func_sig
);
335 record
= (void *)record
+ dd
->finfo_rec_size
;
340 const struct bpf_line_info
*linfo
;
342 linfo
= bpf_prog_linfo__lfind(prog_linfo
, i
, nr_skip
);
344 btf_dump_linfo_plain(btf
, linfo
, "; ",
350 double_insn
= insn
[i
].code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
353 print_bpf_insn(&cbs
, insn
+ i
, true);
357 fprint_hex(stdout
, insn
+ i
, 8, " ");
358 if (double_insn
&& i
< len
- 1) {
360 fprint_hex(stdout
, insn
+ i
+ 1, 8, " ");
367 void dump_xlated_for_graph(struct dump_data
*dd
, void *buf_start
, void *buf_end
,
368 unsigned int start_idx
,
369 bool opcodes
, bool linum
)
371 const struct bpf_insn_cbs cbs
= {
372 .cb_print
= print_insn_for_graph
,
373 .cb_call
= print_call
,
377 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
378 const struct bpf_line_info
*last_linfo
= NULL
;
379 struct bpf_func_info
*record
= dd
->func_info
;
380 struct bpf_insn
*insn_start
= buf_start
;
381 struct bpf_insn
*insn_end
= buf_end
;
382 struct bpf_insn
*cur
= insn_start
;
383 struct btf
*btf
= dd
->btf
;
384 bool double_insn
= false;
387 for (; cur
<= insn_end
; cur
++) {
388 unsigned int insn_off
;
394 double_insn
= cur
->code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
396 insn_off
= (unsigned int)(cur
- insn_start
+ start_idx
);
398 if (record
->insn_off
== insn_off
) {
399 btf_dumper_type_only(btf
, record
->type_id
,
402 if (func_sig
[0] != '\0')
403 printf("; %s:\\l\\\n", func_sig
);
404 record
= (void *)record
+ dd
->finfo_rec_size
;
409 const struct bpf_line_info
*linfo
;
411 linfo
= bpf_prog_linfo__lfind(prog_linfo
, insn_off
, 0);
412 if (linfo
&& linfo
!= last_linfo
) {
413 btf_dump_linfo_dotlabel(btf
, linfo
, linum
);
418 printf("%u: ", insn_off
);
419 print_bpf_insn(&cbs
, cur
, true);
422 printf("\\ \\ \\ \\ ");
423 fprint_hex(stdout
, cur
, 8, " ");
424 if (double_insn
&& cur
<= insn_end
- 1) {
426 fprint_hex(stdout
, cur
+ 1, 8, " ");