Loading...
Note: File does not exist in v4.10.11.
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
49static 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
55void 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) {
73out:
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
100void kernel_syms_destroy(struct dump_data *dd)
101{
102 free(dd->sym_mapping);
103}
104
105static 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
117static 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
126static void
127print_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
157static 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
172static 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 (sym)
178 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
179 "%+d#%s", insn->off, sym->name);
180 else
181 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
182 "%+d#0x%lx", insn->off, address);
183 return dd->scratch_buff;
184}
185
186static const char *print_call_helper(struct dump_data *dd,
187 struct kernel_sym *sym,
188 unsigned long address)
189{
190 if (sym)
191 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
192 "%s", sym->name);
193 else
194 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
195 "0x%lx", address);
196 return dd->scratch_buff;
197}
198
199static const char *print_call(void *private_data,
200 const struct bpf_insn *insn)
201{
202 struct dump_data *dd = private_data;
203 unsigned long address = dd->address_call_base + insn->imm;
204 struct kernel_sym *sym;
205
206 sym = kernel_syms_search(dd, address);
207 if (insn->src_reg == BPF_PSEUDO_CALL)
208 return print_call_pcrel(dd, sym, address, insn);
209 else
210 return print_call_helper(dd, sym, address);
211}
212
213static const char *print_imm(void *private_data,
214 const struct bpf_insn *insn,
215 __u64 full_imm)
216{
217 struct dump_data *dd = private_data;
218
219 if (insn->src_reg == BPF_PSEUDO_MAP_FD)
220 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
221 "map[id:%u]", insn->imm);
222 else
223 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
224 "0x%llx", (unsigned long long)full_imm);
225 return dd->scratch_buff;
226}
227
228void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
229 bool opcodes)
230{
231 const struct bpf_insn_cbs cbs = {
232 .cb_print = print_insn_json,
233 .cb_call = print_call,
234 .cb_imm = print_imm,
235 .private_data = dd,
236 };
237 struct bpf_insn *insn = buf;
238 bool double_insn = false;
239 unsigned int i;
240
241 jsonw_start_array(json_wtr);
242 for (i = 0; i < len / sizeof(*insn); i++) {
243 if (double_insn) {
244 double_insn = false;
245 continue;
246 }
247 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
248
249 jsonw_start_object(json_wtr);
250 jsonw_name(json_wtr, "disasm");
251 print_bpf_insn(&cbs, insn + i, true);
252
253 if (opcodes) {
254 jsonw_name(json_wtr, "opcodes");
255 jsonw_start_object(json_wtr);
256
257 jsonw_name(json_wtr, "code");
258 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
259
260 jsonw_name(json_wtr, "src_reg");
261 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
262
263 jsonw_name(json_wtr, "dst_reg");
264 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
265
266 jsonw_name(json_wtr, "off");
267 print_hex_data_json((uint8_t *)(&insn[i].off), 2);
268
269 jsonw_name(json_wtr, "imm");
270 if (double_insn && i < len - 1)
271 print_hex_data_json((uint8_t *)(&insn[i].imm),
272 12);
273 else
274 print_hex_data_json((uint8_t *)(&insn[i].imm),
275 4);
276 jsonw_end_object(json_wtr);
277 }
278 jsonw_end_object(json_wtr);
279 }
280 jsonw_end_array(json_wtr);
281}
282
283void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
284 bool opcodes)
285{
286 const struct bpf_insn_cbs cbs = {
287 .cb_print = print_insn,
288 .cb_call = print_call,
289 .cb_imm = print_imm,
290 .private_data = dd,
291 };
292 struct bpf_insn *insn = buf;
293 bool double_insn = false;
294 unsigned int i;
295
296 for (i = 0; i < len / sizeof(*insn); i++) {
297 if (double_insn) {
298 double_insn = false;
299 continue;
300 }
301
302 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
303
304 printf("% 4d: ", i);
305 print_bpf_insn(&cbs, insn + i, true);
306
307 if (opcodes) {
308 printf(" ");
309 fprint_hex(stdout, insn + i, 8, " ");
310 if (double_insn && i < len - 1) {
311 printf(" ");
312 fprint_hex(stdout, insn + i + 1, 8, " ");
313 }
314 printf("\n");
315 }
316 }
317}
318
319void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
320 unsigned int start_idx)
321{
322 const struct bpf_insn_cbs cbs = {
323 .cb_print = print_insn_for_graph,
324 .cb_call = print_call,
325 .cb_imm = print_imm,
326 .private_data = dd,
327 };
328 struct bpf_insn *insn_start = buf_start;
329 struct bpf_insn *insn_end = buf_end;
330 struct bpf_insn *cur = insn_start;
331
332 for (; cur <= insn_end; cur++) {
333 printf("% 4d: ", (int)(cur - insn_start + start_idx));
334 print_bpf_insn(&cbs, cur, true);
335 if (cur != insn_end)
336 printf(" | ");
337 }
338}