GNU Linux-libre 6.9.1-gnu
[releases.git] / tools / bpf / bpftool / jit_disasm.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /*
3  * Based on:
4  *
5  * Minimal BPF JIT image disassembler
6  *
7  * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
8  * debugging or verification purposes.
9  *
10  * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
11  * Licensed under the GNU General Public License, version 2.0 (GPLv2)
12  */
13
14 #ifndef _GNU_SOURCE
15 #define _GNU_SOURCE
16 #endif
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <limits.h>
25 #include <bpf/libbpf.h>
26
27 #ifdef HAVE_LLVM_SUPPORT
28 #include <llvm-c/Core.h>
29 #include <llvm-c/Disassembler.h>
30 #include <llvm-c/Target.h>
31 #include <llvm-c/TargetMachine.h>
32 #endif
33
34 #ifdef HAVE_LIBBFD_SUPPORT
35 #include <bfd.h>
36 #include <dis-asm.h>
37 #include <tools/dis-asm-compat.h>
38 #endif
39
40 #include "json_writer.h"
41 #include "main.h"
42
43 static int oper_count;
44
45 #ifdef HAVE_LLVM_SUPPORT
46 #define DISASM_SPACER
47
48 typedef LLVMDisasmContextRef disasm_ctx_t;
49
50 static int printf_json(char *s)
51 {
52         s = strtok(s, " \t");
53         jsonw_string_field(json_wtr, "operation", s);
54
55         jsonw_name(json_wtr, "operands");
56         jsonw_start_array(json_wtr);
57         oper_count = 1;
58
59         while ((s = strtok(NULL, " \t,()")) != 0) {
60                 jsonw_string(json_wtr, s);
61                 oper_count++;
62         }
63         return 0;
64 }
65
66 /* This callback to set the ref_type is necessary to have the LLVM disassembler
67  * print PC-relative addresses instead of byte offsets for branch instruction
68  * targets.
69  */
70 static const char *
71 symbol_lookup_callback(__maybe_unused void *disasm_info,
72                        __maybe_unused uint64_t ref_value,
73                        uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
74                        __maybe_unused const char **ref_name)
75 {
76         *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
77         return NULL;
78 }
79
80 static int
81 init_context(disasm_ctx_t *ctx, const char *arch,
82              __maybe_unused const char *disassembler_options,
83              __maybe_unused unsigned char *image, __maybe_unused ssize_t len)
84 {
85         char *triple;
86
87         if (arch)
88                 triple = LLVMNormalizeTargetTriple(arch);
89         else
90                 triple = LLVMGetDefaultTargetTriple();
91         if (!triple) {
92                 p_err("Failed to retrieve triple");
93                 return -1;
94         }
95         *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
96         LLVMDisposeMessage(triple);
97
98         if (!*ctx) {
99                 p_err("Failed to create disassembler");
100                 return -1;
101         }
102
103         return 0;
104 }
105
106 static void destroy_context(disasm_ctx_t *ctx)
107 {
108         LLVMDisposeMessage(*ctx);
109 }
110
111 static int
112 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc)
113 {
114         char buf[256];
115         int count;
116
117         count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc,
118                                       buf, sizeof(buf));
119         if (json_output)
120                 printf_json(buf);
121         else
122                 printf("%s", buf);
123
124         return count;
125 }
126
127 int disasm_init(void)
128 {
129         LLVMInitializeAllTargetInfos();
130         LLVMInitializeAllTargetMCs();
131         LLVMInitializeAllDisassemblers();
132         return 0;
133 }
134 #endif /* HAVE_LLVM_SUPPORT */
135
136 #ifdef HAVE_LIBBFD_SUPPORT
137 #define DISASM_SPACER "\t"
138
139 typedef struct {
140         struct disassemble_info *info;
141         disassembler_ftype disassemble;
142         bfd *bfdf;
143 } disasm_ctx_t;
144
145 static int get_exec_path(char *tpath, size_t size)
146 {
147         const char *path = "/proc/self/exe";
148         ssize_t len;
149
150         len = readlink(path, tpath, size - 1);
151         if (len <= 0)
152                 return -1;
153
154         tpath[len] = 0;
155
156         return 0;
157 }
158
159 static int printf_json(void *out, const char *fmt, va_list ap)
160 {
161         char *s;
162         int err;
163
164         err = vasprintf(&s, fmt, ap);
165         if (err < 0)
166                 return -1;
167
168         if (!oper_count) {
169                 int i;
170
171                 /* Strip trailing spaces */
172                 i = strlen(s) - 1;
173                 while (s[i] == ' ')
174                         s[i--] = '\0';
175
176                 jsonw_string_field(json_wtr, "operation", s);
177                 jsonw_name(json_wtr, "operands");
178                 jsonw_start_array(json_wtr);
179                 oper_count++;
180         } else if (!strcmp(fmt, ",")) {
181                    /* Skip */
182         } else {
183                 jsonw_string(json_wtr, s);
184                 oper_count++;
185         }
186         free(s);
187         return 0;
188 }
189
190 static int fprintf_json(void *out, const char *fmt, ...)
191 {
192         va_list ap;
193         int r;
194
195         va_start(ap, fmt);
196         r = printf_json(out, fmt, ap);
197         va_end(ap);
198
199         return r;
200 }
201
202 static int fprintf_json_styled(void *out,
203                                enum disassembler_style style __maybe_unused,
204                                const char *fmt, ...)
205 {
206         va_list ap;
207         int r;
208
209         va_start(ap, fmt);
210         r = printf_json(out, fmt, ap);
211         va_end(ap);
212
213         return r;
214 }
215
216 static int init_context(disasm_ctx_t *ctx, const char *arch,
217                         const char *disassembler_options,
218                         unsigned char *image, ssize_t len)
219 {
220         struct disassemble_info *info;
221         char tpath[PATH_MAX];
222         bfd *bfdf;
223
224         memset(tpath, 0, sizeof(tpath));
225         if (get_exec_path(tpath, sizeof(tpath))) {
226                 p_err("failed to create disassembler (get_exec_path)");
227                 return -1;
228         }
229
230         ctx->bfdf = bfd_openr(tpath, NULL);
231         if (!ctx->bfdf) {
232                 p_err("failed to create disassembler (bfd_openr)");
233                 return -1;
234         }
235         if (!bfd_check_format(ctx->bfdf, bfd_object)) {
236                 p_err("failed to create disassembler (bfd_check_format)");
237                 goto err_close;
238         }
239         bfdf = ctx->bfdf;
240
241         ctx->info = malloc(sizeof(struct disassemble_info));
242         if (!ctx->info) {
243                 p_err("mem alloc failed");
244                 goto err_close;
245         }
246         info = ctx->info;
247
248         if (json_output)
249                 init_disassemble_info_compat(info, stdout,
250                                              (fprintf_ftype) fprintf_json,
251                                              fprintf_json_styled);
252         else
253                 init_disassemble_info_compat(info, stdout,
254                                              (fprintf_ftype) fprintf,
255                                              fprintf_styled);
256
257         /* Update architecture info for offload. */
258         if (arch) {
259                 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
260
261                 if (inf) {
262                         bfdf->arch_info = inf;
263                 } else {
264                         p_err("No libbfd support for %s", arch);
265                         goto err_free;
266                 }
267         }
268
269         info->arch = bfd_get_arch(bfdf);
270         info->mach = bfd_get_mach(bfdf);
271         if (disassembler_options)
272                 info->disassembler_options = disassembler_options;
273         info->buffer = image;
274         info->buffer_length = len;
275
276         disassemble_init_for_target(info);
277
278 #ifdef DISASM_FOUR_ARGS_SIGNATURE
279         ctx->disassemble = disassembler(info->arch,
280                                         bfd_big_endian(bfdf),
281                                         info->mach,
282                                         bfdf);
283 #else
284         ctx->disassemble = disassembler(bfdf);
285 #endif
286         if (!ctx->disassemble) {
287                 p_err("failed to create disassembler");
288                 goto err_free;
289         }
290         return 0;
291
292 err_free:
293         free(info);
294 err_close:
295         bfd_close(ctx->bfdf);
296         return -1;
297 }
298
299 static void destroy_context(disasm_ctx_t *ctx)
300 {
301         free(ctx->info);
302         bfd_close(ctx->bfdf);
303 }
304
305 static int
306 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
307                  __maybe_unused ssize_t len, int pc)
308 {
309         return ctx->disassemble(pc, ctx->info);
310 }
311
312 int disasm_init(void)
313 {
314         bfd_init();
315         return 0;
316 }
317 #endif /* HAVE_LIBBPFD_SUPPORT */
318
319 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
320                       const char *arch, const char *disassembler_options,
321                       const struct btf *btf,
322                       const struct bpf_prog_linfo *prog_linfo,
323                       __u64 func_ksym, unsigned int func_idx,
324                       bool linum)
325 {
326         const struct bpf_line_info *linfo = NULL;
327         unsigned int nr_skip = 0;
328         int count, i, pc = 0;
329         disasm_ctx_t ctx;
330
331         if (!len)
332                 return -1;
333
334         if (init_context(&ctx, arch, disassembler_options, image, len))
335                 return -1;
336
337         if (json_output)
338                 jsonw_start_array(json_wtr);
339         do {
340                 if (prog_linfo) {
341                         linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
342                                                                 func_ksym + pc,
343                                                                 func_idx,
344                                                                 nr_skip);
345                         if (linfo)
346                                 nr_skip++;
347                 }
348
349                 if (json_output) {
350                         jsonw_start_object(json_wtr);
351                         oper_count = 0;
352                         if (linfo)
353                                 btf_dump_linfo_json(btf, linfo, linum);
354                         jsonw_name(json_wtr, "pc");
355                         jsonw_printf(json_wtr, "\"0x%x\"", pc);
356                 } else {
357                         if (linfo)
358                                 btf_dump_linfo_plain(btf, linfo, "; ",
359                                                      linum);
360                         printf("%4x:" DISASM_SPACER, pc);
361                 }
362
363                 count = disassemble_insn(&ctx, image, len, pc);
364
365                 if (json_output) {
366                         /* Operand array, was started in fprintf_json. Before
367                          * that, make sure we have a _null_ value if no operand
368                          * other than operation code was present.
369                          */
370                         if (oper_count == 1)
371                                 jsonw_null(json_wtr);
372                         jsonw_end_array(json_wtr);
373                 }
374
375                 if (opcodes) {
376                         if (json_output) {
377                                 jsonw_name(json_wtr, "opcodes");
378                                 jsonw_start_array(json_wtr);
379                                 for (i = 0; i < count; ++i)
380                                         jsonw_printf(json_wtr, "\"0x%02hhx\"",
381                                                      (uint8_t)image[pc + i]);
382                                 jsonw_end_array(json_wtr);
383                         } else {
384                                 printf("\n\t");
385                                 for (i = 0; i < count; ++i)
386                                         printf("%02x ",
387                                                (uint8_t)image[pc + i]);
388                         }
389                 }
390                 if (json_output)
391                         jsonw_end_object(json_wtr);
392                 else
393                         printf("\n");
394
395                 pc += count;
396         } while (count > 0 && pc < len);
397         if (json_output)
398                 jsonw_end_array(json_wtr);
399
400         destroy_context(&ctx);
401
402         return 0;
403 }