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