Loading...
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
43static int oper_count;
44
45#ifdef HAVE_LLVM_SUPPORT
46#define DISASM_SPACER
47
48typedef LLVMDisasmContextRef disasm_ctx_t;
49
50static 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 */
70static const char *
71symbol_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
80static int
81init_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
107static void destroy_context(disasm_ctx_t *ctx)
108{
109 LLVMDisposeMessage(*ctx);
110}
111
112static int
113disassemble_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
129int 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
141struct disasm_info {
142 struct disassemble_info info;
143 __u64 func_ksym;
144};
145
146static 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
154typedef struct {
155 struct disasm_info *info;
156 disassembler_ftype disassemble;
157 bfd *bfdf;
158} disasm_ctx_t;
159
160static 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
174static 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
205static 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
217static 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
231static 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
309err_free:
310 free(info);
311err_close:
312 bfd_close(ctx->bfdf);
313 return -1;
314}
315
316static void destroy_context(disasm_ctx_t *ctx)
317{
318 free(ctx->info);
319 bfd_close(ctx->bfdf);
320}
321
322static int
323disassemble_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
330int disasm_init(void)
331{
332 bfd_init();
333 return 0;
334}
335#endif /* HAVE_LIBBPFD_SUPPORT */
336
337int 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}
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#include <stdarg.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <assert.h>
18#include <unistd.h>
19#include <string.h>
20#include <bfd.h>
21#include <dis-asm.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <limits.h>
25
26#include "json_writer.h"
27#include "main.h"
28
29static void get_exec_path(char *tpath, size_t size)
30{
31 ssize_t len;
32 char *path;
33
34 snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
35 tpath[size - 1] = 0;
36
37 path = strdup(tpath);
38 assert(path);
39
40 len = readlink(path, tpath, size - 1);
41 assert(len > 0);
42 tpath[len] = 0;
43
44 free(path);
45}
46
47static int oper_count;
48static int fprintf_json(void *out, const char *fmt, ...)
49{
50 va_list ap;
51 char *s;
52
53 va_start(ap, fmt);
54 if (!oper_count) {
55 int i;
56
57 s = va_arg(ap, char *);
58
59 /* Strip trailing spaces */
60 i = strlen(s) - 1;
61 while (s[i] == ' ')
62 s[i--] = '\0';
63
64 jsonw_string_field(json_wtr, "operation", s);
65 jsonw_name(json_wtr, "operands");
66 jsonw_start_array(json_wtr);
67 oper_count++;
68 } else if (!strcmp(fmt, ",")) {
69 /* Skip */
70 } else {
71 s = va_arg(ap, char *);
72 jsonw_string(json_wtr, s);
73 oper_count++;
74 }
75 va_end(ap);
76 return 0;
77}
78
79void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
80 const char *arch)
81{
82 disassembler_ftype disassemble;
83 struct disassemble_info info;
84 int count, i, pc = 0;
85 char tpath[PATH_MAX];
86 bfd *bfdf;
87
88 if (!len)
89 return;
90
91 memset(tpath, 0, sizeof(tpath));
92 get_exec_path(tpath, sizeof(tpath));
93
94 bfdf = bfd_openr(tpath, NULL);
95 assert(bfdf);
96 assert(bfd_check_format(bfdf, bfd_object));
97
98 if (json_output)
99 init_disassemble_info(&info, stdout,
100 (fprintf_ftype) fprintf_json);
101 else
102 init_disassemble_info(&info, stdout,
103 (fprintf_ftype) fprintf);
104
105 /* Update architecture info for offload. */
106 if (arch) {
107 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
108
109 if (inf) {
110 bfdf->arch_info = inf;
111 } else {
112 p_err("No libfd support for %s", arch);
113 return;
114 }
115 }
116
117 info.arch = bfd_get_arch(bfdf);
118 info.mach = bfd_get_mach(bfdf);
119 info.buffer = image;
120 info.buffer_length = len;
121
122 disassemble_init_for_target(&info);
123
124#ifdef DISASM_FOUR_ARGS_SIGNATURE
125 disassemble = disassembler(info.arch,
126 bfd_big_endian(bfdf),
127 info.mach,
128 bfdf);
129#else
130 disassemble = disassembler(bfdf);
131#endif
132 assert(disassemble);
133
134 if (json_output)
135 jsonw_start_array(json_wtr);
136 do {
137 if (json_output) {
138 jsonw_start_object(json_wtr);
139 oper_count = 0;
140 jsonw_name(json_wtr, "pc");
141 jsonw_printf(json_wtr, "\"0x%x\"", pc);
142 } else {
143 printf("%4x:\t", pc);
144 }
145
146 count = disassemble(pc, &info);
147 if (json_output) {
148 /* Operand array, was started in fprintf_json. Before
149 * that, make sure we have a _null_ value if no operand
150 * other than operation code was present.
151 */
152 if (oper_count == 1)
153 jsonw_null(json_wtr);
154 jsonw_end_array(json_wtr);
155 }
156
157 if (opcodes) {
158 if (json_output) {
159 jsonw_name(json_wtr, "opcodes");
160 jsonw_start_array(json_wtr);
161 for (i = 0; i < count; ++i)
162 jsonw_printf(json_wtr, "\"0x%02hhx\"",
163 (uint8_t)image[pc + i]);
164 jsonw_end_array(json_wtr);
165 } else {
166 printf("\n\t");
167 for (i = 0; i < count; ++i)
168 printf("%02x ",
169 (uint8_t)image[pc + i]);
170 }
171 }
172 if (json_output)
173 jsonw_end_object(json_wtr);
174 else
175 printf("\n");
176
177 pc += count;
178 } while (count > 0 && pc < len);
179 if (json_output)
180 jsonw_end_array(json_wtr);
181
182 bfd_close(bfdf);
183}