]> Git Repo - linux.git/blob - tools/bpf/bpftool/jit_disasm.c
Linux 6.14-rc3
[linux.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 <[email protected]>
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              __maybe_unused __u64 func_ksym)
85 {
86         char *triple;
87
88         if (arch)
89                 triple = LLVMNormalizeTargetTriple(arch);
90         else
91                 triple = LLVMGetDefaultTargetTriple();
92         if (!triple) {
93                 p_err("Failed to retrieve triple");
94                 return -1;
95         }
96         *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
97         LLVMDisposeMessage(triple);
98
99         if (!*ctx) {
100                 p_err("Failed to create disassembler");
101                 return -1;
102         }
103
104         return 0;
105 }
106
107 static void destroy_context(disasm_ctx_t *ctx)
108 {
109         LLVMDisposeMessage(*ctx);
110 }
111
112 static int
113 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
114                  __u64 func_ksym)
115 {
116         char buf[256];
117         int count;
118
119         count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
120                                       buf, sizeof(buf));
121         if (json_output)
122                 printf_json(buf);
123         else
124                 printf("%s", buf);
125
126         return count;
127 }
128
129 int disasm_init(void)
130 {
131         LLVMInitializeAllTargetInfos();
132         LLVMInitializeAllTargetMCs();
133         LLVMInitializeAllDisassemblers();
134         return 0;
135 }
136 #endif /* HAVE_LLVM_SUPPORT */
137
138 #ifdef HAVE_LIBBFD_SUPPORT
139 #define DISASM_SPACER "\t"
140
141 struct disasm_info {
142         struct disassemble_info info;
143         __u64 func_ksym;
144 };
145
146 static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
147 {
148         struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
149
150         addr += dinfo->func_ksym;
151         generic_print_address(addr, info);
152 }
153
154 typedef struct {
155         struct disasm_info *info;
156         disassembler_ftype disassemble;
157         bfd *bfdf;
158 } disasm_ctx_t;
159
160 static int get_exec_path(char *tpath, size_t size)
161 {
162         const char *path = "/proc/self/exe";
163         ssize_t len;
164
165         len = readlink(path, tpath, size - 1);
166         if (len <= 0)
167                 return -1;
168
169         tpath[len] = 0;
170
171         return 0;
172 }
173
174 static int printf_json(void *out, const char *fmt, va_list ap)
175 {
176         char *s;
177         int err;
178
179         err = vasprintf(&s, fmt, ap);
180         if (err < 0)
181                 return -1;
182
183         if (!oper_count) {
184                 int i;
185
186                 /* Strip trailing spaces */
187                 i = strlen(s) - 1;
188                 while (s[i] == ' ')
189                         s[i--] = '\0';
190
191                 jsonw_string_field(json_wtr, "operation", s);
192                 jsonw_name(json_wtr, "operands");
193                 jsonw_start_array(json_wtr);
194                 oper_count++;
195         } else if (!strcmp(fmt, ",")) {
196                    /* Skip */
197         } else {
198                 jsonw_string(json_wtr, s);
199                 oper_count++;
200         }
201         free(s);
202         return 0;
203 }
204
205 static int fprintf_json(void *out, const char *fmt, ...)
206 {
207         va_list ap;
208         int r;
209
210         va_start(ap, fmt);
211         r = printf_json(out, fmt, ap);
212         va_end(ap);
213
214         return r;
215 }
216
217 static int fprintf_json_styled(void *out,
218                                enum disassembler_style style __maybe_unused,
219                                const char *fmt, ...)
220 {
221         va_list ap;
222         int r;
223
224         va_start(ap, fmt);
225         r = printf_json(out, fmt, ap);
226         va_end(ap);
227
228         return r;
229 }
230
231 static int init_context(disasm_ctx_t *ctx, const char *arch,
232                         const char *disassembler_options,
233                         unsigned char *image, ssize_t len, __u64 func_ksym)
234 {
235         struct disassemble_info *info;
236         char tpath[PATH_MAX];
237         bfd *bfdf;
238
239         memset(tpath, 0, sizeof(tpath));
240         if (get_exec_path(tpath, sizeof(tpath))) {
241                 p_err("failed to create disassembler (get_exec_path)");
242                 return -1;
243         }
244
245         ctx->bfdf = bfd_openr(tpath, NULL);
246         if (!ctx->bfdf) {
247                 p_err("failed to create disassembler (bfd_openr)");
248                 return -1;
249         }
250         if (!bfd_check_format(ctx->bfdf, bfd_object)) {
251                 p_err("failed to create disassembler (bfd_check_format)");
252                 goto err_close;
253         }
254         bfdf = ctx->bfdf;
255
256         ctx->info = malloc(sizeof(struct disasm_info));
257         if (!ctx->info) {
258                 p_err("mem alloc failed");
259                 goto err_close;
260         }
261         ctx->info->func_ksym = func_ksym;
262         info = &ctx->info->info;
263
264         if (json_output)
265                 init_disassemble_info_compat(info, stdout,
266                                              (fprintf_ftype) fprintf_json,
267                                              fprintf_json_styled);
268         else
269                 init_disassemble_info_compat(info, stdout,
270                                              (fprintf_ftype) fprintf,
271                                              fprintf_styled);
272
273         /* Update architecture info for offload. */
274         if (arch) {
275                 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
276
277                 if (inf) {
278                         bfdf->arch_info = inf;
279                 } else {
280                         p_err("No libbfd support for %s", arch);
281                         goto err_free;
282                 }
283         }
284
285         info->arch = bfd_get_arch(bfdf);
286         info->mach = bfd_get_mach(bfdf);
287         if (disassembler_options)
288                 info->disassembler_options = disassembler_options;
289         info->buffer = image;
290         info->buffer_length = len;
291         info->print_address_func = disasm_print_addr;
292
293         disassemble_init_for_target(info);
294
295 #ifdef DISASM_FOUR_ARGS_SIGNATURE
296         ctx->disassemble = disassembler(info->arch,
297                                         bfd_big_endian(bfdf),
298                                         info->mach,
299                                         bfdf);
300 #else
301         ctx->disassemble = disassembler(bfdf);
302 #endif
303         if (!ctx->disassemble) {
304                 p_err("failed to create disassembler");
305                 goto err_free;
306         }
307         return 0;
308
309 err_free:
310         free(info);
311 err_close:
312         bfd_close(ctx->bfdf);
313         return -1;
314 }
315
316 static void destroy_context(disasm_ctx_t *ctx)
317 {
318         free(ctx->info);
319         bfd_close(ctx->bfdf);
320 }
321
322 static int
323 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
324                  __maybe_unused ssize_t len, int pc,
325                  __maybe_unused __u64 func_ksym)
326 {
327         return ctx->disassemble(pc, &ctx->info->info);
328 }
329
330 int disasm_init(void)
331 {
332         bfd_init();
333         return 0;
334 }
335 #endif /* HAVE_LIBBPFD_SUPPORT */
336
337 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
338                       const char *arch, const char *disassembler_options,
339                       const struct btf *btf,
340                       const struct bpf_prog_linfo *prog_linfo,
341                       __u64 func_ksym, unsigned int func_idx,
342                       bool linum)
343 {
344         const struct bpf_line_info *linfo = NULL;
345         unsigned int nr_skip = 0;
346         int count, i, pc = 0;
347         disasm_ctx_t ctx;
348
349         if (!len)
350                 return -1;
351
352         if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
353                 return -1;
354
355         if (json_output)
356                 jsonw_start_array(json_wtr);
357         do {
358                 if (prog_linfo) {
359                         linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
360                                                                 func_ksym + pc,
361                                                                 func_idx,
362                                                                 nr_skip);
363                         if (linfo)
364                                 nr_skip++;
365                 }
366
367                 if (json_output) {
368                         jsonw_start_object(json_wtr);
369                         oper_count = 0;
370                         if (linfo)
371                                 btf_dump_linfo_json(btf, linfo, linum);
372                         jsonw_name(json_wtr, "pc");
373                         jsonw_printf(json_wtr, "\"0x%x\"", pc);
374                 } else {
375                         if (linfo)
376                                 btf_dump_linfo_plain(btf, linfo, "; ",
377                                                      linum);
378                         printf("%4x:" DISASM_SPACER, pc);
379                 }
380
381                 count = disassemble_insn(&ctx, image, len, pc, func_ksym);
382
383                 if (json_output) {
384                         /* Operand array, was started in fprintf_json. Before
385                          * that, make sure we have a _null_ value if no operand
386                          * other than operation code was present.
387                          */
388                         if (oper_count == 1)
389                                 jsonw_null(json_wtr);
390                         jsonw_end_array(json_wtr);
391                 }
392
393                 if (opcodes) {
394                         if (json_output) {
395                                 jsonw_name(json_wtr, "opcodes");
396                                 jsonw_start_array(json_wtr);
397                                 for (i = 0; i < count; ++i)
398                                         jsonw_printf(json_wtr, "\"0x%02hhx\"",
399                                                      (uint8_t)image[pc + i]);
400                                 jsonw_end_array(json_wtr);
401                         } else {
402                                 printf("\n\t");
403                                 for (i = 0; i < count; ++i)
404                                         printf("%02x ",
405                                                (uint8_t)image[pc + i]);
406                         }
407                 }
408                 if (json_output)
409                         jsonw_end_object(json_wtr);
410                 else
411                         printf("\n");
412
413                 pc += count;
414         } while (count > 0 && pc < len);
415         if (json_output)
416                 jsonw_end_array(json_wtr);
417
418         destroy_context(&ctx);
419
420         return 0;
421 }
This page took 0.055583 seconds and 4 git commands to generate.