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 #define _GNU_SOURCE
15 #include <stdio.h>
16 #include <stdarg.h>
17 #include <stdint.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/stat.h>
25 #include <limits.h>
26 #include <bpf/libbpf.h>
27
28 #include "json_writer.h"
29 #include "main.h"
30
get_exec_path(char * tpath,size_t size)31 static void get_exec_path(char *tpath, size_t size)
32 {
33 const char *path = "/proc/self/exe";
34 ssize_t len;
35
36 len = readlink(path, tpath, size - 1);
37 assert(len > 0);
38 tpath[len] = 0;
39 }
40
41 static int oper_count;
fprintf_json(void * out,const char * fmt,...)42 static int fprintf_json(void *out, const char *fmt, ...)
43 {
44 va_list ap;
45 char *s;
46 int err;
47
48 va_start(ap, fmt);
49 err = vasprintf(&s, fmt, ap);
50 va_end(ap);
51 if (err < 0)
52 return -1;
53
54 if (!oper_count) {
55 int i;
56
57 /* Strip trailing spaces */
58 i = strlen(s) - 1;
59 while (s[i] == ' ')
60 s[i--] = '\0';
61
62 jsonw_string_field(json_wtr, "operation", s);
63 jsonw_name(json_wtr, "operands");
64 jsonw_start_array(json_wtr);
65 oper_count++;
66 } else if (!strcmp(fmt, ",")) {
67 /* Skip */
68 } else {
69 jsonw_string(json_wtr, s);
70 oper_count++;
71 }
72 free(s);
73 return 0;
74 }
75
disasm_print_insn(unsigned char * image,ssize_t len,int opcodes,const char * arch,const char * disassembler_options,const struct btf * btf,const struct bpf_prog_linfo * prog_linfo,__u64 func_ksym,unsigned int func_idx,bool linum)76 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
77 const char *arch, const char *disassembler_options,
78 const struct btf *btf,
79 const struct bpf_prog_linfo *prog_linfo,
80 __u64 func_ksym, unsigned int func_idx,
81 bool linum)
82 {
83 const struct bpf_line_info *linfo = NULL;
84 disassembler_ftype disassemble;
85 struct disassemble_info info;
86 unsigned int nr_skip = 0;
87 int count, i, pc = 0;
88 char tpath[PATH_MAX];
89 bfd *bfdf;
90
91 if (!len)
92 return;
93
94 memset(tpath, 0, sizeof(tpath));
95 get_exec_path(tpath, sizeof(tpath));
96
97 bfdf = bfd_openr(tpath, NULL);
98 assert(bfdf);
99 assert(bfd_check_format(bfdf, bfd_object));
100
101 if (json_output)
102 init_disassemble_info(&info, stdout,
103 (fprintf_ftype) fprintf_json);
104 else
105 init_disassemble_info(&info, stdout,
106 (fprintf_ftype) fprintf);
107
108 /* Update architecture info for offload. */
109 if (arch) {
110 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
111
112 if (inf) {
113 bfdf->arch_info = inf;
114 } else {
115 p_err("No libbfd support for %s", arch);
116 return;
117 }
118 }
119
120 info.arch = bfd_get_arch(bfdf);
121 info.mach = bfd_get_mach(bfdf);
122 if (disassembler_options)
123 info.disassembler_options = disassembler_options;
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 (prog_linfo) {
143 linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
144 func_ksym + pc,
145 func_idx,
146 nr_skip);
147 if (linfo)
148 nr_skip++;
149 }
150
151 if (json_output) {
152 jsonw_start_object(json_wtr);
153 oper_count = 0;
154 if (linfo)
155 btf_dump_linfo_json(btf, linfo, linum);
156 jsonw_name(json_wtr, "pc");
157 jsonw_printf(json_wtr, "\"0x%x\"", pc);
158 } else {
159 if (linfo)
160 btf_dump_linfo_plain(btf, linfo, "; ",
161 linum);
162 printf("%4x:\t", pc);
163 }
164
165 count = disassemble(pc, &info);
166 if (json_output) {
167 /* Operand array, was started in fprintf_json. Before
168 * that, make sure we have a _null_ value if no operand
169 * other than operation code was present.
170 */
171 if (oper_count == 1)
172 jsonw_null(json_wtr);
173 jsonw_end_array(json_wtr);
174 }
175
176 if (opcodes) {
177 if (json_output) {
178 jsonw_name(json_wtr, "opcodes");
179 jsonw_start_array(json_wtr);
180 for (i = 0; i < count; ++i)
181 jsonw_printf(json_wtr, "\"0x%02hhx\"",
182 (uint8_t)image[pc + i]);
183 jsonw_end_array(json_wtr);
184 } else {
185 printf("\n\t");
186 for (i = 0; i < count; ++i)
187 printf("%02x ",
188 (uint8_t)image[pc + i]);
189 }
190 }
191 if (json_output)
192 jsonw_end_object(json_wtr);
193 else
194 printf("\n");
195
196 pc += count;
197 } while (count > 0 && pc < len);
198 if (json_output)
199 jsonw_end_array(json_wtr);
200
201 bfd_close(bfdf);
202 }
203
disasm_init(void)204 int disasm_init(void)
205 {
206 bfd_init();
207 return 0;
208 }
209