diff options
Diffstat (limited to 'tools/bpf/bpftool/xlated_dumper.c')
-rw-r--r-- | tools/bpf/bpftool/xlated_dumper.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c new file mode 100644 index 000000000000..dfcdf794c9d1 --- /dev/null +++ b/tools/bpf/bpftool/xlated_dumper.c | |||
@@ -0,0 +1,286 @@ | |||
1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
2 | /* | ||
3 | * Copyright (C) 2018 Netronome Systems, Inc. | ||
4 | * | ||
5 | * This software is dual licensed under the GNU General License Version 2, | ||
6 | * June 1991 as shown in the file COPYING in the top-level directory of this | ||
7 | * source tree or the BSD 2-Clause License provided below. You have the | ||
8 | * option to license this software under the complete terms of either license. | ||
9 | * | ||
10 | * The BSD 2-Clause License: | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or | ||
13 | * without modification, are permitted provided that the following | ||
14 | * conditions are met: | ||
15 | * | ||
16 | * 1. Redistributions of source code must retain the above | ||
17 | * copyright notice, this list of conditions and the following | ||
18 | * disclaimer. | ||
19 | * | ||
20 | * 2. Redistributions in binary form must reproduce the above | ||
21 | * copyright notice, this list of conditions and the following | ||
22 | * disclaimer in the documentation and/or other materials | ||
23 | * provided with the distribution. | ||
24 | * | ||
25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
26 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
29 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
33 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
35 | * POSSIBILITY OF SUCH DAMAGE. | ||
36 | */ | ||
37 | |||
38 | #include <stdarg.h> | ||
39 | #include <stdio.h> | ||
40 | #include <stdlib.h> | ||
41 | #include <string.h> | ||
42 | #include <sys/types.h> | ||
43 | |||
44 | #include "disasm.h" | ||
45 | #include "json_writer.h" | ||
46 | #include "main.h" | ||
47 | #include "xlated_dumper.h" | ||
48 | |||
49 | static int kernel_syms_cmp(const void *sym_a, const void *sym_b) | ||
50 | { | ||
51 | return ((struct kernel_sym *)sym_a)->address - | ||
52 | ((struct kernel_sym *)sym_b)->address; | ||
53 | } | ||
54 | |||
55 | void kernel_syms_load(struct dump_data *dd) | ||
56 | { | ||
57 | struct kernel_sym *sym; | ||
58 | char buff[256]; | ||
59 | void *tmp, *address; | ||
60 | FILE *fp; | ||
61 | |||
62 | fp = fopen("/proc/kallsyms", "r"); | ||
63 | if (!fp) | ||
64 | return; | ||
65 | |||
66 | while (!feof(fp)) { | ||
67 | if (!fgets(buff, sizeof(buff), fp)) | ||
68 | break; | ||
69 | tmp = realloc(dd->sym_mapping, | ||
70 | (dd->sym_count + 1) * | ||
71 | sizeof(*dd->sym_mapping)); | ||
72 | if (!tmp) { | ||
73 | out: | ||
74 | free(dd->sym_mapping); | ||
75 | dd->sym_mapping = NULL; | ||
76 | fclose(fp); | ||
77 | return; | ||
78 | } | ||
79 | dd->sym_mapping = tmp; | ||
80 | sym = &dd->sym_mapping[dd->sym_count]; | ||
81 | if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) | ||
82 | continue; | ||
83 | sym->address = (unsigned long)address; | ||
84 | if (!strcmp(sym->name, "__bpf_call_base")) { | ||
85 | dd->address_call_base = sym->address; | ||
86 | /* sysctl kernel.kptr_restrict was set */ | ||
87 | if (!sym->address) | ||
88 | goto out; | ||
89 | } | ||
90 | if (sym->address) | ||
91 | dd->sym_count++; | ||
92 | } | ||
93 | |||
94 | fclose(fp); | ||
95 | |||
96 | qsort(dd->sym_mapping, dd->sym_count, | ||
97 | sizeof(*dd->sym_mapping), kernel_syms_cmp); | ||
98 | } | ||
99 | |||
100 | void kernel_syms_destroy(struct dump_data *dd) | ||
101 | { | ||
102 | free(dd->sym_mapping); | ||
103 | } | ||
104 | |||
105 | static struct kernel_sym *kernel_syms_search(struct dump_data *dd, | ||
106 | unsigned long key) | ||
107 | { | ||
108 | struct kernel_sym sym = { | ||
109 | .address = key, | ||
110 | }; | ||
111 | |||
112 | return dd->sym_mapping ? | ||
113 | bsearch(&sym, dd->sym_mapping, dd->sym_count, | ||
114 | sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; | ||
115 | } | ||
116 | |||
117 | static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) | ||
118 | { | ||
119 | va_list args; | ||
120 | |||
121 | va_start(args, fmt); | ||
122 | vprintf(fmt, args); | ||
123 | va_end(args); | ||
124 | } | ||
125 | |||
126 | static void print_insn_json(struct bpf_verifier_env *env, 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 (sym) | ||
147 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
148 | "%+d#%s", insn->off, sym->name); | ||
149 | else | ||
150 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
151 | "%+d#0x%lx", insn->off, address); | ||
152 | return dd->scratch_buff; | ||
153 | } | ||
154 | |||
155 | static const char *print_call_helper(struct dump_data *dd, | ||
156 | struct kernel_sym *sym, | ||
157 | unsigned long address) | ||
158 | { | ||
159 | if (sym) | ||
160 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
161 | "%s", sym->name); | ||
162 | else | ||
163 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
164 | "0x%lx", address); | ||
165 | return dd->scratch_buff; | ||
166 | } | ||
167 | |||
168 | static const char *print_call(void *private_data, | ||
169 | const struct bpf_insn *insn) | ||
170 | { | ||
171 | struct dump_data *dd = private_data; | ||
172 | unsigned long address = dd->address_call_base + insn->imm; | ||
173 | struct kernel_sym *sym; | ||
174 | |||
175 | sym = kernel_syms_search(dd, address); | ||
176 | if (insn->src_reg == BPF_PSEUDO_CALL) | ||
177 | return print_call_pcrel(dd, sym, address, insn); | ||
178 | else | ||
179 | return print_call_helper(dd, sym, address); | ||
180 | } | ||
181 | |||
182 | static const char *print_imm(void *private_data, | ||
183 | const struct bpf_insn *insn, | ||
184 | __u64 full_imm) | ||
185 | { | ||
186 | struct dump_data *dd = private_data; | ||
187 | |||
188 | if (insn->src_reg == BPF_PSEUDO_MAP_FD) | ||
189 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
190 | "map[id:%u]", insn->imm); | ||
191 | else | ||
192 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
193 | "0x%llx", (unsigned long long)full_imm); | ||
194 | return dd->scratch_buff; | ||
195 | } | ||
196 | |||
197 | void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, | ||
198 | bool opcodes) | ||
199 | { | ||
200 | const struct bpf_insn_cbs cbs = { | ||
201 | .cb_print = print_insn_json, | ||
202 | .cb_call = print_call, | ||
203 | .cb_imm = print_imm, | ||
204 | .private_data = dd, | ||
205 | }; | ||
206 | struct bpf_insn *insn = buf; | ||
207 | bool double_insn = false; | ||
208 | unsigned int i; | ||
209 | |||
210 | jsonw_start_array(json_wtr); | ||
211 | for (i = 0; i < len / sizeof(*insn); i++) { | ||
212 | if (double_insn) { | ||
213 | double_insn = false; | ||
214 | continue; | ||
215 | } | ||
216 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | ||
217 | |||
218 | jsonw_start_object(json_wtr); | ||
219 | jsonw_name(json_wtr, "disasm"); | ||
220 | print_bpf_insn(&cbs, NULL, insn + i, true); | ||
221 | |||
222 | if (opcodes) { | ||
223 | jsonw_name(json_wtr, "opcodes"); | ||
224 | jsonw_start_object(json_wtr); | ||
225 | |||
226 | jsonw_name(json_wtr, "code"); | ||
227 | jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); | ||
228 | |||
229 | jsonw_name(json_wtr, "src_reg"); | ||
230 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); | ||
231 | |||
232 | jsonw_name(json_wtr, "dst_reg"); | ||
233 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); | ||
234 | |||
235 | jsonw_name(json_wtr, "off"); | ||
236 | print_hex_data_json((uint8_t *)(&insn[i].off), 2); | ||
237 | |||
238 | jsonw_name(json_wtr, "imm"); | ||
239 | if (double_insn && i < len - 1) | ||
240 | print_hex_data_json((uint8_t *)(&insn[i].imm), | ||
241 | 12); | ||
242 | else | ||
243 | print_hex_data_json((uint8_t *)(&insn[i].imm), | ||
244 | 4); | ||
245 | jsonw_end_object(json_wtr); | ||
246 | } | ||
247 | jsonw_end_object(json_wtr); | ||
248 | } | ||
249 | jsonw_end_array(json_wtr); | ||
250 | } | ||
251 | |||
252 | void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, | ||
253 | bool opcodes) | ||
254 | { | ||
255 | const struct bpf_insn_cbs cbs = { | ||
256 | .cb_print = print_insn, | ||
257 | .cb_call = print_call, | ||
258 | .cb_imm = print_imm, | ||
259 | .private_data = dd, | ||
260 | }; | ||
261 | struct bpf_insn *insn = buf; | ||
262 | bool double_insn = false; | ||
263 | unsigned int i; | ||
264 | |||
265 | for (i = 0; i < len / sizeof(*insn); i++) { | ||
266 | if (double_insn) { | ||
267 | double_insn = false; | ||
268 | continue; | ||
269 | } | ||
270 | |||
271 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | ||
272 | |||
273 | printf("% 4d: ", i); | ||
274 | print_bpf_insn(&cbs, NULL, insn + i, true); | ||
275 | |||
276 | if (opcodes) { | ||
277 | printf(" "); | ||
278 | fprint_hex(stdout, insn + i, 8, " "); | ||
279 | if (double_insn && i < len - 1) { | ||
280 | printf(" "); | ||
281 | fprint_hex(stdout, insn + i + 1, 8, " "); | ||
282 | } | ||
283 | printf("\n"); | ||
284 | } | ||
285 | } | ||
286 | } | ||