Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/bpf/bpftool/xlated_dumper.c
26285 views
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
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)
54
continue;
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 */
59
if (!sym->address)
60
goto out;
61
}
62
if (sym->address)
63
dd->sym_count++;
64
}
65
66
fclose(fp);
67
68
qsort(dd->sym_mapping, dd->sym_count,
69
sizeof(*dd->sym_mapping), kernel_syms_cmp);
70
}
71
72
void kernel_syms_destroy(struct dump_data *dd)
73
{
74
free(dd->sym_mapping);
75
}
76
77
struct kernel_sym *kernel_syms_search(struct dump_data *dd,
78
unsigned long key)
79
{
80
struct kernel_sym sym = {
81
.address = key,
82
};
83
84
return dd->sym_mapping ?
85
bsearch(&sym, dd->sym_mapping, dd->sym_count,
86
sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
87
}
88
89
static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
90
{
91
va_list args;
92
93
va_start(args, fmt);
94
vprintf(fmt, args);
95
va_end(args);
96
}
97
98
static void __printf(2, 3)
99
print_insn_for_graph(void *private_data, const char *fmt, ...)
100
{
101
char buf[64], *p;
102
va_list args;
103
104
va_start(args, fmt);
105
vsnprintf(buf, sizeof(buf), fmt, args);
106
va_end(args);
107
108
p = buf;
109
while (*p != '\0') {
110
if (*p == '\n') {
111
memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
112
/* Align each instruction dump row left. */
113
*p++ = '\\';
114
*p++ = 'l';
115
/* Output multiline concatenation. */
116
*p++ = '\\';
117
} else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
118
memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
119
/* Escape special character. */
120
*p++ = '\\';
121
}
122
123
p++;
124
}
125
126
printf("%s", buf);
127
}
128
129
static void __printf(2, 3)
130
print_insn_json(void *private_data, const char *fmt, ...)
131
{
132
unsigned int l = strlen(fmt);
133
char chomped_fmt[l];
134
va_list args;
135
136
va_start(args, fmt);
137
if (l > 0) {
138
strncpy(chomped_fmt, fmt, l - 1);
139
chomped_fmt[l - 1] = '\0';
140
}
141
jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
142
va_end(args);
143
}
144
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)
149
{
150
if (!dd->nr_jited_ksyms)
151
/* Do not show address for interpreted programs */
152
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
153
"%+d", insn->off);
154
else if (sym)
155
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
156
"%+d#%s", insn->off, sym->name);
157
else
158
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
159
"%+d#0x%lx", insn->off, address);
160
return dd->scratch_buff;
161
}
162
163
static const char *print_call_helper(struct dump_data *dd,
164
struct kernel_sym *sym,
165
unsigned long address)
166
{
167
if (sym)
168
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
169
"%s", sym->name);
170
else
171
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
172
"0x%lx", address);
173
return dd->scratch_buff;
174
}
175
176
static const char *print_call(void *private_data,
177
const struct bpf_insn *insn)
178
{
179
struct dump_data *dd = private_data;
180
unsigned long address = dd->address_call_base + insn->imm;
181
struct kernel_sym *sym;
182
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];
186
187
sym = kernel_syms_search(dd, address);
188
if (insn->src_reg == BPF_PSEUDO_CALL)
189
return print_call_pcrel(dd, sym, address, insn);
190
else
191
return print_call_helper(dd, sym, address);
192
}
193
194
static const char *print_imm(void *private_data,
195
const struct bpf_insn *insn,
196
__u64 full_imm)
197
{
198
struct dump_data *dd = private_data;
199
200
if (insn->src_reg == BPF_PSEUDO_MAP_FD)
201
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
202
"map[id:%d]", insn->imm);
203
else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
204
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
205
"map[id:%d][0]+%d", 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:%d]+%d", 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);
212
else
213
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
214
"0x%llx", (unsigned long long)full_imm);
215
return dd->scratch_buff;
216
}
217
218
void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
219
bool opcodes, bool linum)
220
{
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,
225
.cb_imm = print_imm,
226
.private_data = dd,
227
};
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;
233
char func_sig[1024];
234
unsigned int i;
235
236
jsonw_start_array(json_wtr);
237
record = dd->func_info;
238
for (i = 0; i < len / sizeof(*insn); i++) {
239
if (double_insn) {
240
double_insn = false;
241
continue;
242
}
243
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
244
245
jsonw_start_object(json_wtr);
246
247
if (btf && record) {
248
if (record->insn_off == i) {
249
btf_dumper_type_only(btf, record->type_id,
250
func_sig,
251
sizeof(func_sig));
252
if (func_sig[0] != '\0') {
253
jsonw_name(json_wtr, "proto");
254
jsonw_string(json_wtr, func_sig);
255
}
256
record = (void *)record + dd->finfo_rec_size;
257
}
258
}
259
260
if (prog_linfo) {
261
const struct bpf_line_info *linfo;
262
263
linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
264
if (linfo) {
265
btf_dump_linfo_json(btf, linfo, linum);
266
nr_skip++;
267
}
268
}
269
270
jsonw_name(json_wtr, "disasm");
271
print_bpf_insn(&cbs, insn + i, true);
272
273
if (opcodes) {
274
jsonw_name(json_wtr, "opcodes");
275
jsonw_start_object(json_wtr);
276
277
jsonw_name(json_wtr, "code");
278
jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
279
280
jsonw_name(json_wtr, "src_reg");
281
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
282
283
jsonw_name(json_wtr, "dst_reg");
284
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
285
286
jsonw_name(json_wtr, "off");
287
print_hex_data_json((uint8_t *)(&insn[i].off), 2);
288
289
jsonw_name(json_wtr, "imm");
290
if (double_insn && i < len - 1)
291
print_hex_data_json((uint8_t *)(&insn[i].imm),
292
12);
293
else
294
print_hex_data_json((uint8_t *)(&insn[i].imm),
295
4);
296
jsonw_end_object(json_wtr);
297
}
298
jsonw_end_object(json_wtr);
299
}
300
jsonw_end_array(json_wtr);
301
}
302
303
void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
304
bool opcodes, bool linum)
305
{
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,
310
.cb_imm = print_imm,
311
.private_data = dd,
312
};
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;
318
char func_sig[1024];
319
unsigned int i;
320
321
record = dd->func_info;
322
for (i = 0; i < len / sizeof(*insn); i++) {
323
if (double_insn) {
324
double_insn = false;
325
continue;
326
}
327
328
if (btf && record) {
329
if (record->insn_off == i) {
330
btf_dumper_type_only(btf, record->type_id,
331
func_sig,
332
sizeof(func_sig));
333
if (func_sig[0] != '\0')
334
printf("%s:\n", func_sig);
335
record = (void *)record + dd->finfo_rec_size;
336
}
337
}
338
339
if (prog_linfo) {
340
const struct bpf_line_info *linfo;
341
342
linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
343
if (linfo) {
344
btf_dump_linfo_plain(btf, linfo, "; ",
345
linum);
346
nr_skip++;
347
}
348
}
349
350
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
351
352
printf("%4u: ", i);
353
print_bpf_insn(&cbs, insn + i, true);
354
355
if (opcodes) {
356
printf(" ");
357
fprint_hex(stdout, insn + i, 8, " ");
358
if (double_insn && i < len - 1) {
359
printf(" ");
360
fprint_hex(stdout, insn + i + 1, 8, " ");
361
}
362
printf("\n");
363
}
364
}
365
}
366
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)
370
{
371
const struct bpf_insn_cbs cbs = {
372
.cb_print = print_insn_for_graph,
373
.cb_call = print_call,
374
.cb_imm = print_imm,
375
.private_data = dd,
376
};
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;
385
char func_sig[1024];
386
387
for (; cur <= insn_end; cur++) {
388
unsigned int insn_off;
389
390
if (double_insn) {
391
double_insn = false;
392
continue;
393
}
394
double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
395
396
insn_off = (unsigned int)(cur - insn_start + start_idx);
397
if (btf && record) {
398
if (record->insn_off == insn_off) {
399
btf_dumper_type_only(btf, record->type_id,
400
func_sig,
401
sizeof(func_sig));
402
if (func_sig[0] != '\0')
403
printf("; %s:\\l\\\n", func_sig);
404
record = (void *)record + dd->finfo_rec_size;
405
}
406
}
407
408
if (prog_linfo) {
409
const struct bpf_line_info *linfo;
410
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);
414
last_linfo = linfo;
415
}
416
}
417
418
printf("%u: ", insn_off);
419
print_bpf_insn(&cbs, cur, true);
420
421
if (opcodes) {
422
printf("\\ \\ \\ \\ ");
423
fprint_hex(stdout, cur, 8, " ");
424
if (double_insn && cur <= insn_end - 1) {
425
printf(" ");
426
fprint_hex(stdout, cur + 1, 8, " ");
427
}
428
printf("\\l\\\n");
429
}
430
431
if (cur != insn_end)
432
printf("| ");
433
}
434
}
435
436