blob: 3ef3093560bae88c11b5a3226a55735502bc6306 [file] [log] [blame]
Jakub Kicinski907b2232018-12-12 19:59:26 -08001// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
Jakub Kicinski71bb4282017-10-04 20:10:04 -07002/*
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
Quentin Monnet107f0412017-10-23 09:24:09 -070014#include <stdarg.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070015#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <assert.h>
19#include <unistd.h>
20#include <string.h>
21#include <bfd.h>
22#include <dis-asm.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070023#include <sys/stat.h>
Prashant Bholecdc89c92017-11-02 17:09:45 +090024#include <limits.h>
Martin KaFai Laub053b432018-12-07 16:42:32 -080025#include <libbpf.h>
Jakub Kicinski71bb4282017-10-04 20:10:04 -070026
Quentin Monnet107f0412017-10-23 09:24:09 -070027#include "json_writer.h"
28#include "main.h"
29
Jakub Kicinski71bb4282017-10-04 20:10:04 -070030static void get_exec_path(char *tpath, size_t size)
31{
Quentin Monnet327e5da2018-11-30 16:25:44 +000032 const char *path = "/proc/self/exe";
Jakub Kicinski71bb4282017-10-04 20:10:04 -070033 ssize_t len;
Jakub Kicinski71bb4282017-10-04 20:10:04 -070034
35 len = readlink(path, tpath, size - 1);
36 assert(len > 0);
37 tpath[len] = 0;
Jakub Kicinski71bb4282017-10-04 20:10:04 -070038}
39
Quentin Monnet107f0412017-10-23 09:24:09 -070040static int oper_count;
41static int fprintf_json(void *out, const char *fmt, ...)
42{
43 va_list ap;
44 char *s;
45
46 va_start(ap, fmt);
47 if (!oper_count) {
48 int i;
49
50 s = va_arg(ap, char *);
51
52 /* Strip trailing spaces */
53 i = strlen(s) - 1;
54 while (s[i] == ' ')
55 s[i--] = '\0';
56
57 jsonw_string_field(json_wtr, "operation", s);
58 jsonw_name(json_wtr, "operands");
59 jsonw_start_array(json_wtr);
60 oper_count++;
61 } else if (!strcmp(fmt, ",")) {
62 /* Skip */
63 } else {
64 s = va_arg(ap, char *);
65 jsonw_string(json_wtr, s);
66 oper_count++;
67 }
68 va_end(ap);
69 return 0;
70}
71
Jiong Wange6593592018-01-16 16:05:21 -080072void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
Martin KaFai Laub053b432018-12-07 16:42:32 -080073 const char *arch, const char *disassembler_options,
74 const struct btf *btf,
75 const struct bpf_prog_linfo *prog_linfo,
76 __u64 func_ksym, unsigned int func_idx,
77 bool linum)
Jakub Kicinski71bb4282017-10-04 20:10:04 -070078{
Martin KaFai Laub053b432018-12-07 16:42:32 -080079 const struct bpf_line_info *linfo = NULL;
Jakub Kicinski71bb4282017-10-04 20:10:04 -070080 disassembler_ftype disassemble;
81 struct disassemble_info info;
Martin KaFai Laub053b432018-12-07 16:42:32 -080082 unsigned int nr_skip = 0;
Jakub Kicinski71bb4282017-10-04 20:10:04 -070083 int count, i, pc = 0;
Prashant Bholecdc89c92017-11-02 17:09:45 +090084 char tpath[PATH_MAX];
Jakub Kicinski71bb4282017-10-04 20:10:04 -070085 bfd *bfdf;
86
87 if (!len)
88 return;
89
90 memset(tpath, 0, sizeof(tpath));
91 get_exec_path(tpath, sizeof(tpath));
92
93 bfdf = bfd_openr(tpath, NULL);
94 assert(bfdf);
95 assert(bfd_check_format(bfdf, bfd_object));
96
Quentin Monnet107f0412017-10-23 09:24:09 -070097 if (json_output)
98 init_disassemble_info(&info, stdout,
99 (fprintf_ftype) fprintf_json);
100 else
101 init_disassemble_info(&info, stdout,
102 (fprintf_ftype) fprintf);
Jiong Wange6593592018-01-16 16:05:21 -0800103
104 /* Update architecture info for offload. */
105 if (arch) {
106 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
107
108 if (inf) {
109 bfdf->arch_info = inf;
110 } else {
Stanislav Fomichev29a9c102018-11-12 13:44:10 -0800111 p_err("No libbfd support for %s", arch);
Jiong Wange6593592018-01-16 16:05:21 -0800112 return;
113 }
114 }
115
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700116 info.arch = bfd_get_arch(bfdf);
117 info.mach = bfd_get_mach(bfdf);
Jakub Kicinski3ddeac62018-10-18 11:34:55 -0700118 if (disassembler_options)
119 info.disassembler_options = disassembler_options;
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700120 info.buffer = image;
121 info.buffer_length = len;
122
123 disassemble_init_for_target(&info);
124
Roman Gushchinfb982662017-12-27 19:16:29 +0000125#ifdef DISASM_FOUR_ARGS_SIGNATURE
126 disassemble = disassembler(info.arch,
127 bfd_big_endian(bfdf),
128 info.mach,
129 bfdf);
130#else
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700131 disassemble = disassembler(bfdf);
Roman Gushchinfb982662017-12-27 19:16:29 +0000132#endif
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700133 assert(disassemble);
134
Quentin Monnet107f0412017-10-23 09:24:09 -0700135 if (json_output)
136 jsonw_start_array(json_wtr);
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700137 do {
Martin KaFai Laub053b432018-12-07 16:42:32 -0800138 if (prog_linfo) {
139 linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
140 func_ksym + pc,
141 func_idx,
142 nr_skip);
143 if (linfo)
144 nr_skip++;
145 }
146
Quentin Monnet107f0412017-10-23 09:24:09 -0700147 if (json_output) {
148 jsonw_start_object(json_wtr);
149 oper_count = 0;
Martin KaFai Laub053b432018-12-07 16:42:32 -0800150 if (linfo)
151 btf_dump_linfo_json(btf, linfo, linum);
Quentin Monnet107f0412017-10-23 09:24:09 -0700152 jsonw_name(json_wtr, "pc");
153 jsonw_printf(json_wtr, "\"0x%x\"", pc);
154 } else {
Martin KaFai Laub053b432018-12-07 16:42:32 -0800155 if (linfo)
156 btf_dump_linfo_plain(btf, linfo, "; ",
157 linum);
Quentin Monnet107f0412017-10-23 09:24:09 -0700158 printf("%4x:\t", pc);
159 }
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700160
161 count = disassemble(pc, &info);
Quentin Monnet107f0412017-10-23 09:24:09 -0700162 if (json_output) {
163 /* Operand array, was started in fprintf_json. Before
164 * that, make sure we have a _null_ value if no operand
165 * other than operation code was present.
166 */
167 if (oper_count == 1)
168 jsonw_null(json_wtr);
169 jsonw_end_array(json_wtr);
170 }
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700171
172 if (opcodes) {
Quentin Monnet107f0412017-10-23 09:24:09 -0700173 if (json_output) {
174 jsonw_name(json_wtr, "opcodes");
175 jsonw_start_array(json_wtr);
176 for (i = 0; i < count; ++i)
177 jsonw_printf(json_wtr, "\"0x%02hhx\"",
178 (uint8_t)image[pc + i]);
179 jsonw_end_array(json_wtr);
180 } else {
181 printf("\n\t");
182 for (i = 0; i < count; ++i)
183 printf("%02x ",
184 (uint8_t)image[pc + i]);
185 }
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700186 }
Quentin Monnet107f0412017-10-23 09:24:09 -0700187 if (json_output)
188 jsonw_end_object(json_wtr);
189 else
190 printf("\n");
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700191
192 pc += count;
193 } while (count > 0 && pc < len);
Quentin Monnet107f0412017-10-23 09:24:09 -0700194 if (json_output)
195 jsonw_end_array(json_wtr);
Jakub Kicinski71bb4282017-10-04 20:10:04 -0700196
197 bfd_close(bfdf);
198}
Stanislav Fomichev29a9c102018-11-12 13:44:10 -0800199
200int disasm_init(void)
201{
202 bfd_init();
203 return 0;
204}