GNU Linux-libre 4.19.304-gnu1
[releases.git] / tools / bpf / bpftool / jit_disasm.c
1 /*
2  * Based on:
3  *
4  * Minimal BPF JIT image disassembler
5  *
6  * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
7  * debugging or verification purposes.
8  *
9  * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
10  * Licensed under the GNU General Public License, version 2.0 (GPLv2)
11  */
12
13 #define _GNU_SOURCE
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <assert.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <bfd.h>
23 #include <dis-asm.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <limits.h>
27
28 #include "json_writer.h"
29 #include "main.h"
30
31 static void get_exec_path(char *tpath, size_t size)
32 {
33         ssize_t len;
34         char *path;
35
36         snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
37         tpath[size - 1] = 0;
38
39         path = strdup(tpath);
40         assert(path);
41
42         len = readlink(path, tpath, size - 1);
43         assert(len > 0);
44         tpath[len] = 0;
45
46         free(path);
47 }
48
49 static int oper_count;
50 static int fprintf_json(void *out, const char *fmt, ...)
51 {
52         va_list ap;
53         char *s;
54         int err;
55
56         va_start(ap, fmt);
57         err = vasprintf(&s, fmt, ap);
58         va_end(ap);
59         if (err < 0)
60                 return -1;
61
62         if (!oper_count) {
63                 int i;
64
65                 /* Strip trailing spaces */
66                 i = strlen(s) - 1;
67                 while (s[i] == ' ')
68                         s[i--] = '\0';
69
70                 jsonw_string_field(json_wtr, "operation", s);
71                 jsonw_name(json_wtr, "operands");
72                 jsonw_start_array(json_wtr);
73                 oper_count++;
74         } else if (!strcmp(fmt, ",")) {
75                    /* Skip */
76         } else {
77                 jsonw_string(json_wtr, s);
78                 oper_count++;
79         }
80         free(s);
81         return 0;
82 }
83
84 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
85                        const char *arch)
86 {
87         disassembler_ftype disassemble;
88         struct disassemble_info info;
89         int count, i, pc = 0;
90         char tpath[PATH_MAX];
91         bfd *bfdf;
92
93         if (!len)
94                 return;
95
96         memset(tpath, 0, sizeof(tpath));
97         get_exec_path(tpath, sizeof(tpath));
98
99         bfdf = bfd_openr(tpath, NULL);
100         assert(bfdf);
101         assert(bfd_check_format(bfdf, bfd_object));
102
103         if (json_output)
104                 init_disassemble_info(&info, stdout,
105                                       (fprintf_ftype) fprintf_json);
106         else
107                 init_disassemble_info(&info, stdout,
108                                       (fprintf_ftype) fprintf);
109
110         /* Update architecture info for offload. */
111         if (arch) {
112                 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
113
114                 if (inf) {
115                         bfdf->arch_info = inf;
116                 } else {
117                         p_err("No libfd support for %s", arch);
118                         return;
119                 }
120         }
121
122         info.arch = bfd_get_arch(bfdf);
123         info.mach = bfd_get_mach(bfdf);
124         info.buffer = image;
125         info.buffer_length = len;
126
127         disassemble_init_for_target(&info);
128
129 #ifdef DISASM_FOUR_ARGS_SIGNATURE
130         disassemble = disassembler(info.arch,
131                                    bfd_big_endian(bfdf),
132                                    info.mach,
133                                    bfdf);
134 #else
135         disassemble = disassembler(bfdf);
136 #endif
137         assert(disassemble);
138
139         if (json_output)
140                 jsonw_start_array(json_wtr);
141         do {
142                 if (json_output) {
143                         jsonw_start_object(json_wtr);
144                         oper_count = 0;
145                         jsonw_name(json_wtr, "pc");
146                         jsonw_printf(json_wtr, "\"0x%x\"", pc);
147                 } else {
148                         printf("%4x:\t", pc);
149                 }
150
151                 count = disassemble(pc, &info);
152                 if (json_output) {
153                         /* Operand array, was started in fprintf_json. Before
154                          * that, make sure we have a _null_ value if no operand
155                          * other than operation code was present.
156                          */
157                         if (oper_count == 1)
158                                 jsonw_null(json_wtr);
159                         jsonw_end_array(json_wtr);
160                 }
161
162                 if (opcodes) {
163                         if (json_output) {
164                                 jsonw_name(json_wtr, "opcodes");
165                                 jsonw_start_array(json_wtr);
166                                 for (i = 0; i < count; ++i)
167                                         jsonw_printf(json_wtr, "\"0x%02hhx\"",
168                                                      (uint8_t)image[pc + i]);
169                                 jsonw_end_array(json_wtr);
170                         } else {
171                                 printf("\n\t");
172                                 for (i = 0; i < count; ++i)
173                                         printf("%02x ",
174                                                (uint8_t)image[pc + i]);
175                         }
176                 }
177                 if (json_output)
178                         jsonw_end_object(json_wtr);
179                 else
180                         printf("\n");
181
182                 pc += count;
183         } while (count > 0 && pc < len);
184         if (json_output)
185                 jsonw_end_array(json_wtr);
186
187         bfd_close(bfdf);
188 }