1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2018 Netronome Systems, Inc. */
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");
35 if (!fgets(buff
, sizeof(buff
), fp
))
37 tmp
= reallocarray(dd
->sym_mapping
, dd
->sym_count
+ 1,
38 sizeof(*dd
->sym_mapping
));
41 free(dd
->sym_mapping
);
42 dd
->sym_mapping
= NULL
;
46 dd
->sym_mapping
= tmp
;
47 sym
= &dd
->sym_mapping
[dd
->sym_count
];
48 if (sscanf(buff
, "%p %*c %s", &address
, sym
->name
) != 2)
50 sym
->address
= (unsigned long)address
;
51 if (!strcmp(sym
->name
, "__bpf_call_base")) {
52 dd
->address_call_base
= sym
->address
;
53 /* sysctl kernel.kptr_restrict was set */
63 qsort(dd
->sym_mapping
, dd
->sym_count
,
64 sizeof(*dd
->sym_mapping
), kernel_syms_cmp
);
67 void kernel_syms_destroy(struct dump_data
*dd
)
69 free(dd
->sym_mapping
);
72 struct kernel_sym
*kernel_syms_search(struct dump_data
*dd
,
75 struct kernel_sym sym
= {
79 return dd
->sym_mapping
?
80 bsearch(&sym
, dd
->sym_mapping
, dd
->sym_count
,
81 sizeof(*dd
->sym_mapping
), kernel_syms_cmp
) : NULL
;
84 static void __printf(2, 3) print_insn(void *private_data
, const char *fmt
, ...)
93 static void __printf(2, 3)
94 print_insn_for_graph(void *private_data
, const char *fmt
, ...)
100 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
106 memmove(p
+ 3, p
, strlen(buf
) + 1 - (p
- buf
));
107 /* Align each instruction dump row left. */
110 /* Output multiline concatenation. */
112 } else if (*p
== '<' || *p
== '>' || *p
== '|' || *p
== '&') {
113 memmove(p
+ 1, p
, strlen(buf
) + 1 - (p
- buf
));
114 /* Escape special character. */
124 static void __printf(2, 3)
125 print_insn_json(void *private_data
, const char *fmt
, ...)
127 unsigned int l
= strlen(fmt
);
133 strncpy(chomped_fmt
, fmt
, l
- 1);
134 chomped_fmt
[l
- 1] = '\0';
136 jsonw_vprintf_enquote(json_wtr
, chomped_fmt
, args
);
140 static const char *print_call_pcrel(struct dump_data
*dd
,
141 struct kernel_sym
*sym
,
142 unsigned long address
,
143 const struct bpf_insn
*insn
)
145 if (!dd
->nr_jited_ksyms
)
146 /* Do not show address for interpreted programs */
147 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
150 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
151 "%+d#%s", insn
->off
, sym
->name
);
153 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
154 "%+d#0x%lx", insn
->off
, address
);
155 return dd
->scratch_buff
;
158 static const char *print_call_helper(struct dump_data
*dd
,
159 struct kernel_sym
*sym
,
160 unsigned long address
)
163 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
166 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
168 return dd
->scratch_buff
;
171 static const char *print_call(void *private_data
,
172 const struct bpf_insn
*insn
)
174 struct dump_data
*dd
= private_data
;
175 unsigned long address
= dd
->address_call_base
+ insn
->imm
;
176 struct kernel_sym
*sym
;
178 if (insn
->src_reg
== BPF_PSEUDO_CALL
&&
179 (__u32
) insn
->imm
< dd
->nr_jited_ksyms
)
180 address
= dd
->jited_ksyms
[insn
->imm
];
182 sym
= kernel_syms_search(dd
, address
);
183 if (insn
->src_reg
== BPF_PSEUDO_CALL
)
184 return print_call_pcrel(dd
, sym
, address
, insn
);
186 return print_call_helper(dd
, sym
, address
);
189 static const char *print_imm(void *private_data
,
190 const struct bpf_insn
*insn
,
193 struct dump_data
*dd
= private_data
;
195 if (insn
->src_reg
== BPF_PSEUDO_MAP_FD
)
196 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
197 "map[id:%u]", insn
->imm
);
199 snprintf(dd
->scratch_buff
, sizeof(dd
->scratch_buff
),
200 "0x%llx", (unsigned long long)full_imm
);
201 return dd
->scratch_buff
;
204 void dump_xlated_json(struct dump_data
*dd
, void *buf
, unsigned int len
,
205 bool opcodes
, bool linum
)
207 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
208 const struct bpf_insn_cbs cbs
= {
209 .cb_print
= print_insn_json
,
210 .cb_call
= print_call
,
214 struct bpf_func_info
*record
;
215 struct bpf_insn
*insn
= buf
;
216 struct btf
*btf
= dd
->btf
;
217 bool double_insn
= false;
218 unsigned int nr_skip
= 0;
222 jsonw_start_array(json_wtr
);
223 record
= dd
->func_info
;
224 for (i
= 0; i
< len
/ sizeof(*insn
); i
++) {
229 double_insn
= insn
[i
].code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
231 jsonw_start_object(json_wtr
);
234 if (record
->insn_off
== i
) {
235 btf_dumper_type_only(btf
, record
->type_id
,
238 if (func_sig
[0] != '\0') {
239 jsonw_name(json_wtr
, "proto");
240 jsonw_string(json_wtr
, func_sig
);
242 record
= (void *)record
+ dd
->finfo_rec_size
;
247 const struct bpf_line_info
*linfo
;
249 linfo
= bpf_prog_linfo__lfind(prog_linfo
, i
, nr_skip
);
251 btf_dump_linfo_json(btf
, linfo
, linum
);
256 jsonw_name(json_wtr
, "disasm");
257 print_bpf_insn(&cbs
, insn
+ i
, true);
260 jsonw_name(json_wtr
, "opcodes");
261 jsonw_start_object(json_wtr
);
263 jsonw_name(json_wtr
, "code");
264 jsonw_printf(json_wtr
, "\"0x%02hhx\"", insn
[i
].code
);
266 jsonw_name(json_wtr
, "src_reg");
267 jsonw_printf(json_wtr
, "\"0x%hhx\"", insn
[i
].src_reg
);
269 jsonw_name(json_wtr
, "dst_reg");
270 jsonw_printf(json_wtr
, "\"0x%hhx\"", insn
[i
].dst_reg
);
272 jsonw_name(json_wtr
, "off");
273 print_hex_data_json((uint8_t *)(&insn
[i
].off
), 2);
275 jsonw_name(json_wtr
, "imm");
276 if (double_insn
&& i
< len
- 1)
277 print_hex_data_json((uint8_t *)(&insn
[i
].imm
),
280 print_hex_data_json((uint8_t *)(&insn
[i
].imm
),
282 jsonw_end_object(json_wtr
);
284 jsonw_end_object(json_wtr
);
286 jsonw_end_array(json_wtr
);
289 void dump_xlated_plain(struct dump_data
*dd
, void *buf
, unsigned int len
,
290 bool opcodes
, bool linum
)
292 const struct bpf_prog_linfo
*prog_linfo
= dd
->prog_linfo
;
293 const struct bpf_insn_cbs cbs
= {
294 .cb_print
= print_insn
,
295 .cb_call
= print_call
,
299 struct bpf_func_info
*record
;
300 struct bpf_insn
*insn
= buf
;
301 struct btf
*btf
= dd
->btf
;
302 unsigned int nr_skip
= 0;
303 bool double_insn
= false;
307 record
= dd
->func_info
;
308 for (i
= 0; i
< len
/ sizeof(*insn
); i
++) {
315 if (record
->insn_off
== i
) {
316 btf_dumper_type_only(btf
, record
->type_id
,
319 if (func_sig
[0] != '\0')
320 printf("%s:\n", func_sig
);
321 record
= (void *)record
+ dd
->finfo_rec_size
;
326 const struct bpf_line_info
*linfo
;
328 linfo
= bpf_prog_linfo__lfind(prog_linfo
, i
, nr_skip
);
330 btf_dump_linfo_plain(btf
, linfo
, "; ",
336 double_insn
= insn
[i
].code
== (BPF_LD
| BPF_IMM
| BPF_DW
);
339 print_bpf_insn(&cbs
, insn
+ i
, true);
343 fprint_hex(stdout
, insn
+ i
, 8, " ");
344 if (double_insn
&& i
< len
- 1) {
346 fprint_hex(stdout
, insn
+ i
+ 1, 8, " ");
353 void dump_xlated_for_graph(struct dump_data
*dd
, void *buf_start
, void *buf_end
,
354 unsigned int start_idx
)
356 const struct bpf_insn_cbs cbs
= {
357 .cb_print
= print_insn_for_graph
,
358 .cb_call
= print_call
,
362 struct bpf_insn
*insn_start
= buf_start
;
363 struct bpf_insn
*insn_end
= buf_end
;
364 struct bpf_insn
*cur
= insn_start
;
366 for (; cur
<= insn_end
; cur
++) {
367 printf("% 4d: ", (int)(cur
- insn_start
+ start_idx
));
368 print_bpf_insn(&cbs
, cur
, true);