]> Git Repo - linux.git/blob - tools/bpf/bpftool/xlated_dumper.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[linux.git] / tools / bpf / bpftool / xlated_dumper.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2018 Netronome Systems, Inc. */
3
4 #ifndef _GNU_SOURCE
5 #define _GNU_SOURCE
6 #endif
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <bpf/libbpf.h>
13 #include <bpf/libbpf_internal.h>
14
15 #include "disasm.h"
16 #include "json_writer.h"
17 #include "main.h"
18 #include "xlated_dumper.h"
19
20 static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
21 {
22         return ((struct kernel_sym *)sym_a)->address -
23                ((struct kernel_sym *)sym_b)->address;
24 }
25
26 void kernel_syms_load(struct dump_data *dd)
27 {
28         struct kernel_sym *sym;
29         char buff[256];
30         void *tmp, *address;
31         FILE *fp;
32
33         fp = fopen("/proc/kallsyms", "r");
34         if (!fp)
35                 return;
36
37         while (fgets(buff, sizeof(buff), fp)) {
38                 tmp = libbpf_reallocarray(dd->sym_mapping, dd->sym_count + 1,
39                                           sizeof(*dd->sym_mapping));
40                 if (!tmp) {
41 out:
42                         free(dd->sym_mapping);
43                         dd->sym_mapping = NULL;
44                         fclose(fp);
45                         return;
46                 }
47                 dd->sym_mapping = tmp;
48                 sym = &dd->sym_mapping[dd->sym_count];
49                 if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2)
50                         continue;
51                 sym->address = (unsigned long)address;
52                 if (!strcmp(sym->name, "__bpf_call_base")) {
53                         dd->address_call_base = sym->address;
54                         /* sysctl kernel.kptr_restrict was set */
55                         if (!sym->address)
56                                 goto out;
57                 }
58                 if (sym->address)
59                         dd->sym_count++;
60         }
61
62         fclose(fp);
63
64         qsort(dd->sym_mapping, dd->sym_count,
65               sizeof(*dd->sym_mapping), kernel_syms_cmp);
66 }
67
68 void kernel_syms_destroy(struct dump_data *dd)
69 {
70         free(dd->sym_mapping);
71 }
72
73 struct kernel_sym *kernel_syms_search(struct dump_data *dd,
74                                       unsigned long key)
75 {
76         struct kernel_sym sym = {
77                 .address = key,
78         };
79
80         return dd->sym_mapping ?
81                bsearch(&sym, dd->sym_mapping, dd->sym_count,
82                        sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
83 }
84
85 static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
86 {
87         va_list args;
88
89         va_start(args, fmt);
90         vprintf(fmt, args);
91         va_end(args);
92 }
93
94 static void __printf(2, 3)
95 print_insn_for_graph(void *private_data, const char *fmt, ...)
96 {
97         char buf[64], *p;
98         va_list args;
99
100         va_start(args, fmt);
101         vsnprintf(buf, sizeof(buf), fmt, args);
102         va_end(args);
103
104         p = buf;
105         while (*p != '\0') {
106                 if (*p == '\n') {
107                         memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
108                         /* Align each instruction dump row left. */
109                         *p++ = '\\';
110                         *p++ = 'l';
111                         /* Output multiline concatenation. */
112                         *p++ = '\\';
113                 } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
114                         memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
115                         /* Escape special character. */
116                         *p++ = '\\';
117                 }
118
119                 p++;
120         }
121
122         printf("%s", buf);
123 }
124
125 static void __printf(2, 3)
126 print_insn_json(void *private_data, const char *fmt, ...)
127 {
128         unsigned int l = strlen(fmt);
129         char chomped_fmt[l];
130         va_list args;
131
132         va_start(args, fmt);
133         if (l > 0) {
134                 strncpy(chomped_fmt, fmt, l - 1);
135                 chomped_fmt[l - 1] = '\0';
136         }
137         jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
138         va_end(args);
139 }
140
141 static const char *print_call_pcrel(struct dump_data *dd,
142                                     struct kernel_sym *sym,
143                                     unsigned long address,
144                                     const struct bpf_insn *insn)
145 {
146         if (!dd->nr_jited_ksyms)
147                 /* Do not show address for interpreted programs */
148                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
149                         "%+d", insn->off);
150         else if (sym)
151                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
152                          "%+d#%s", insn->off, sym->name);
153         else
154                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
155                          "%+d#0x%lx", insn->off, address);
156         return dd->scratch_buff;
157 }
158
159 static const char *print_call_helper(struct dump_data *dd,
160                                      struct kernel_sym *sym,
161                                      unsigned long address)
162 {
163         if (sym)
164                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
165                          "%s", sym->name);
166         else
167                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
168                          "0x%lx", address);
169         return dd->scratch_buff;
170 }
171
172 static const char *print_call(void *private_data,
173                               const struct bpf_insn *insn)
174 {
175         struct dump_data *dd = private_data;
176         unsigned long address = dd->address_call_base + insn->imm;
177         struct kernel_sym *sym;
178
179         if (insn->src_reg == BPF_PSEUDO_CALL &&
180             (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
181                 address = dd->jited_ksyms[insn->imm];
182
183         sym = kernel_syms_search(dd, address);
184         if (insn->src_reg == BPF_PSEUDO_CALL)
185                 return print_call_pcrel(dd, sym, address, insn);
186         else
187                 return print_call_helper(dd, sym, address);
188 }
189
190 static const char *print_imm(void *private_data,
191                              const struct bpf_insn *insn,
192                              __u64 full_imm)
193 {
194         struct dump_data *dd = private_data;
195
196         if (insn->src_reg == BPF_PSEUDO_MAP_FD)
197                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
198                          "map[id:%u]", insn->imm);
199         else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
200                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
201                          "map[id:%u][0]+%u", insn->imm, (insn + 1)->imm);
202         else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
203                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
204                          "map[idx:%u]+%u", insn->imm, (insn + 1)->imm);
205         else if (insn->src_reg == BPF_PSEUDO_FUNC)
206                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
207                          "subprog[%+d]", insn->imm);
208         else
209                 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
210                          "0x%llx", (unsigned long long)full_imm);
211         return dd->scratch_buff;
212 }
213
214 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
215                       bool opcodes, bool linum)
216 {
217         const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
218         const struct bpf_insn_cbs cbs = {
219                 .cb_print       = print_insn_json,
220                 .cb_call        = print_call,
221                 .cb_imm         = print_imm,
222                 .private_data   = dd,
223         };
224         struct bpf_func_info *record;
225         struct bpf_insn *insn = buf;
226         struct btf *btf = dd->btf;
227         bool double_insn = false;
228         unsigned int nr_skip = 0;
229         char func_sig[1024];
230         unsigned int i;
231
232         jsonw_start_array(json_wtr);
233         record = dd->func_info;
234         for (i = 0; i < len / sizeof(*insn); i++) {
235                 if (double_insn) {
236                         double_insn = false;
237                         continue;
238                 }
239                 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
240
241                 jsonw_start_object(json_wtr);
242
243                 if (btf && record) {
244                         if (record->insn_off == i) {
245                                 btf_dumper_type_only(btf, record->type_id,
246                                                      func_sig,
247                                                      sizeof(func_sig));
248                                 if (func_sig[0] != '\0') {
249                                         jsonw_name(json_wtr, "proto");
250                                         jsonw_string(json_wtr, func_sig);
251                                 }
252                                 record = (void *)record + dd->finfo_rec_size;
253                         }
254                 }
255
256                 if (prog_linfo) {
257                         const struct bpf_line_info *linfo;
258
259                         linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
260                         if (linfo) {
261                                 btf_dump_linfo_json(btf, linfo, linum);
262                                 nr_skip++;
263                         }
264                 }
265
266                 jsonw_name(json_wtr, "disasm");
267                 print_bpf_insn(&cbs, insn + i, true);
268
269                 if (opcodes) {
270                         jsonw_name(json_wtr, "opcodes");
271                         jsonw_start_object(json_wtr);
272
273                         jsonw_name(json_wtr, "code");
274                         jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
275
276                         jsonw_name(json_wtr, "src_reg");
277                         jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
278
279                         jsonw_name(json_wtr, "dst_reg");
280                         jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
281
282                         jsonw_name(json_wtr, "off");
283                         print_hex_data_json((uint8_t *)(&insn[i].off), 2);
284
285                         jsonw_name(json_wtr, "imm");
286                         if (double_insn && i < len - 1)
287                                 print_hex_data_json((uint8_t *)(&insn[i].imm),
288                                                     12);
289                         else
290                                 print_hex_data_json((uint8_t *)(&insn[i].imm),
291                                                     4);
292                         jsonw_end_object(json_wtr);
293                 }
294                 jsonw_end_object(json_wtr);
295         }
296         jsonw_end_array(json_wtr);
297 }
298
299 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
300                        bool opcodes, bool linum)
301 {
302         const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
303         const struct bpf_insn_cbs cbs = {
304                 .cb_print       = print_insn,
305                 .cb_call        = print_call,
306                 .cb_imm         = print_imm,
307                 .private_data   = dd,
308         };
309         struct bpf_func_info *record;
310         struct bpf_insn *insn = buf;
311         struct btf *btf = dd->btf;
312         unsigned int nr_skip = 0;
313         bool double_insn = false;
314         char func_sig[1024];
315         unsigned int i;
316
317         record = dd->func_info;
318         for (i = 0; i < len / sizeof(*insn); i++) {
319                 if (double_insn) {
320                         double_insn = false;
321                         continue;
322                 }
323
324                 if (btf && record) {
325                         if (record->insn_off == i) {
326                                 btf_dumper_type_only(btf, record->type_id,
327                                                      func_sig,
328                                                      sizeof(func_sig));
329                                 if (func_sig[0] != '\0')
330                                         printf("%s:\n", func_sig);
331                                 record = (void *)record + dd->finfo_rec_size;
332                         }
333                 }
334
335                 if (prog_linfo) {
336                         const struct bpf_line_info *linfo;
337
338                         linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
339                         if (linfo) {
340                                 btf_dump_linfo_plain(btf, linfo, "; ",
341                                                      linum);
342                                 nr_skip++;
343                         }
344                 }
345
346                 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
347
348                 printf("% 4d: ", i);
349                 print_bpf_insn(&cbs, insn + i, true);
350
351                 if (opcodes) {
352                         printf("       ");
353                         fprint_hex(stdout, insn + i, 8, " ");
354                         if (double_insn && i < len - 1) {
355                                 printf(" ");
356                                 fprint_hex(stdout, insn + i + 1, 8, " ");
357                         }
358                         printf("\n");
359                 }
360         }
361 }
362
363 void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
364                            unsigned int start_idx,
365                            bool opcodes, bool linum)
366 {
367         const struct bpf_insn_cbs cbs = {
368                 .cb_print       = print_insn_for_graph,
369                 .cb_call        = print_call,
370                 .cb_imm         = print_imm,
371                 .private_data   = dd,
372         };
373         const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
374         const struct bpf_line_info *last_linfo = NULL;
375         struct bpf_func_info *record = dd->func_info;
376         struct bpf_insn *insn_start = buf_start;
377         struct bpf_insn *insn_end = buf_end;
378         struct bpf_insn *cur = insn_start;
379         struct btf *btf = dd->btf;
380         bool double_insn = false;
381         char func_sig[1024];
382
383         for (; cur <= insn_end; cur++) {
384                 unsigned int insn_off;
385
386                 if (double_insn) {
387                         double_insn = false;
388                         continue;
389                 }
390                 double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
391
392                 insn_off = (unsigned int)(cur - insn_start + start_idx);
393                 if (btf && record) {
394                         if (record->insn_off == insn_off) {
395                                 btf_dumper_type_only(btf, record->type_id,
396                                                      func_sig,
397                                                      sizeof(func_sig));
398                                 if (func_sig[0] != '\0')
399                                         printf("; %s:\\l\\\n", func_sig);
400                                 record = (void *)record + dd->finfo_rec_size;
401                         }
402                 }
403
404                 if (prog_linfo) {
405                         const struct bpf_line_info *linfo;
406
407                         linfo = bpf_prog_linfo__lfind(prog_linfo, insn_off, 0);
408                         if (linfo && linfo != last_linfo) {
409                                 btf_dump_linfo_dotlabel(btf, linfo, linum);
410                                 last_linfo = linfo;
411                         }
412                 }
413
414                 printf("%d: ", insn_off);
415                 print_bpf_insn(&cbs, cur, true);
416
417                 if (opcodes) {
418                         printf("\\ \\ \\ \\ ");
419                         fprint_hex(stdout, cur, 8, " ");
420                         if (double_insn && cur <= insn_end - 1) {
421                                 printf(" ");
422                                 fprint_hex(stdout, cur + 1, 8, " ");
423                         }
424                         printf("\\l\\\n");
425                 }
426
427                 if (cur != insn_end)
428                         printf("| ");
429         }
430 }
This page took 0.055758 seconds and 4 git commands to generate.