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 #define _GNU_SOURCE
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/types.h>
44
45 #include "disasm.h"
46 #include "json_writer.h"
47 #include "main.h"
48 #include "xlated_dumper.h"
49
kernel_syms_cmp(const void * sym_a,const void * sym_b)50 static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
51 {
52 return ((struct kernel_sym *)sym_a)->address -
53 ((struct kernel_sym *)sym_b)->address;
54 }
55
kernel_syms_load(struct dump_data * dd)56 void kernel_syms_load(struct dump_data *dd)
57 {
58 struct kernel_sym *sym;
59 char buff[256];
60 void *tmp, *address;
61 FILE *fp;
62
63 fp = fopen("/proc/kallsyms", "r");
64 if (!fp)
65 return;
66
67 while (!feof(fp)) {
68 if (!fgets(buff, sizeof(buff), fp))
69 break;
70 tmp = reallocarray(dd->sym_mapping, 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
kernel_syms_destroy(struct dump_data * dd)100 void kernel_syms_destroy(struct dump_data *dd)
101 {
102 free(dd->sym_mapping);
103 }
104
kernel_syms_search(struct dump_data * dd,unsigned long key)105 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
print_insn(void * private_data,const char * fmt,...)117 static void print_insn(void *private_data, 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_for_graph(void * private_data,const char * fmt,...)127 print_insn_for_graph(void *private_data, const char *fmt, ...)
128 {
129 char buf[64], *p;
130 va_list args;
131
132 va_start(args, fmt);
133 vsnprintf(buf, sizeof(buf), fmt, args);
134 va_end(args);
135
136 p = buf;
137 while (*p != '\0') {
138 if (*p == '\n') {
139 memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
140 /* Align each instruction dump row left. */
141 *p++ = '\\';
142 *p++ = 'l';
143 /* Output multiline concatenation. */
144 *p++ = '\\';
145 } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
146 memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
147 /* Escape special character. */
148 *p++ = '\\';
149 }
150
151 p++;
152 }
153
154 printf("%s", buf);
155 }
156
print_insn_json(void * private_data,const char * fmt,...)157 static void print_insn_json(void *private_data, const char *fmt, ...)
158 {
159 unsigned int l = strlen(fmt);
160 char chomped_fmt[l];
161 va_list args;
162
163 va_start(args, fmt);
164 if (l > 0) {
165 strncpy(chomped_fmt, fmt, l - 1);
166 chomped_fmt[l - 1] = '\0';
167 }
168 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
169 va_end(args);
170 }
171
print_call_pcrel(struct dump_data * dd,struct kernel_sym * sym,unsigned long address,const struct bpf_insn * insn)172 static const char *print_call_pcrel(struct dump_data *dd,
173 struct kernel_sym *sym,
174 unsigned long address,
175 const struct bpf_insn *insn)
176 {
177 if (!dd->nr_jited_ksyms)
178 /* Do not show address for interpreted programs */
179 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
180 "%+d", insn->off);
181 else if (sym)
182 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
183 "%+d#%s", insn->off, sym->name);
184 else
185 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
186 "%+d#0x%lx", insn->off, address);
187 return dd->scratch_buff;
188 }
189
print_call_helper(struct dump_data * dd,struct kernel_sym * sym,unsigned long address)190 static const char *print_call_helper(struct dump_data *dd,
191 struct kernel_sym *sym,
192 unsigned long address)
193 {
194 if (sym)
195 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
196 "%s", sym->name);
197 else
198 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
199 "0x%lx", address);
200 return dd->scratch_buff;
201 }
202
print_call(void * private_data,const struct bpf_insn * insn)203 static const char *print_call(void *private_data,
204 const struct bpf_insn *insn)
205 {
206 struct dump_data *dd = private_data;
207 unsigned long address = dd->address_call_base + insn->imm;
208 struct kernel_sym *sym;
209
210 if (insn->src_reg == BPF_PSEUDO_CALL &&
211 (__u32) insn->imm < dd->nr_jited_ksyms)
212 address = dd->jited_ksyms[insn->imm];
213
214 sym = kernel_syms_search(dd, address);
215 if (insn->src_reg == BPF_PSEUDO_CALL)
216 return print_call_pcrel(dd, sym, address, insn);
217 else
218 return print_call_helper(dd, sym, address);
219 }
220
print_imm(void * private_data,const struct bpf_insn * insn,__u64 full_imm)221 static const char *print_imm(void *private_data,
222 const struct bpf_insn *insn,
223 __u64 full_imm)
224 {
225 struct dump_data *dd = private_data;
226
227 if (insn->src_reg == BPF_PSEUDO_MAP_FD)
228 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
229 "map[id:%u]", insn->imm);
230 else
231 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
232 "0x%llx", (unsigned long long)full_imm);
233 return dd->scratch_buff;
234 }
235
dump_xlated_json(struct dump_data * dd,void * buf,unsigned int len,bool opcodes)236 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
237 bool opcodes)
238 {
239 const struct bpf_insn_cbs cbs = {
240 .cb_print = print_insn_json,
241 .cb_call = print_call,
242 .cb_imm = print_imm,
243 .private_data = dd,
244 };
245 struct bpf_insn *insn = buf;
246 bool double_insn = false;
247 unsigned int i;
248
249 jsonw_start_array(json_wtr);
250 for (i = 0; i < len / sizeof(*insn); i++) {
251 if (double_insn) {
252 double_insn = false;
253 continue;
254 }
255 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
256
257 jsonw_start_object(json_wtr);
258 jsonw_name(json_wtr, "disasm");
259 print_bpf_insn(&cbs, insn + i, true);
260
261 if (opcodes) {
262 jsonw_name(json_wtr, "opcodes");
263 jsonw_start_object(json_wtr);
264
265 jsonw_name(json_wtr, "code");
266 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
267
268 jsonw_name(json_wtr, "src_reg");
269 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
270
271 jsonw_name(json_wtr, "dst_reg");
272 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
273
274 jsonw_name(json_wtr, "off");
275 print_hex_data_json((uint8_t *)(&insn[i].off), 2);
276
277 jsonw_name(json_wtr, "imm");
278 if (double_insn && i < len - 1)
279 print_hex_data_json((uint8_t *)(&insn[i].imm),
280 12);
281 else
282 print_hex_data_json((uint8_t *)(&insn[i].imm),
283 4);
284 jsonw_end_object(json_wtr);
285 }
286 jsonw_end_object(json_wtr);
287 }
288 jsonw_end_array(json_wtr);
289 }
290
dump_xlated_plain(struct dump_data * dd,void * buf,unsigned int len,bool opcodes)291 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
292 bool opcodes)
293 {
294 const struct bpf_insn_cbs cbs = {
295 .cb_print = print_insn,
296 .cb_call = print_call,
297 .cb_imm = print_imm,
298 .private_data = dd,
299 };
300 struct bpf_insn *insn = buf;
301 bool double_insn = false;
302 unsigned int i;
303
304 for (i = 0; i < len / sizeof(*insn); i++) {
305 if (double_insn) {
306 double_insn = false;
307 continue;
308 }
309
310 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
311
312 printf("% 4d: ", i);
313 print_bpf_insn(&cbs, insn + i, true);
314
315 if (opcodes) {
316 printf(" ");
317 fprint_hex(stdout, insn + i, 8, " ");
318 if (double_insn && i < len - 1) {
319 printf(" ");
320 fprint_hex(stdout, insn + i + 1, 8, " ");
321 }
322 printf("\n");
323 }
324 }
325 }
326
dump_xlated_for_graph(struct dump_data * dd,void * buf_start,void * buf_end,unsigned int start_idx)327 void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
328 unsigned int start_idx)
329 {
330 const struct bpf_insn_cbs cbs = {
331 .cb_print = print_insn_for_graph,
332 .cb_call = print_call,
333 .cb_imm = print_imm,
334 .private_data = dd,
335 };
336 struct bpf_insn *insn_start = buf_start;
337 struct bpf_insn *insn_end = buf_end;
338 struct bpf_insn *cur = insn_start;
339
340 for (; cur <= insn_end; cur++) {
341 printf("% 4d: ", (int)(cur - insn_start + start_idx));
342 print_bpf_insn(&cbs, cur, true);
343 if (cur != insn_end)
344 printf(" | ");
345 }
346 }
347