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 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; 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 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 204 int disasm_init(void) 205 { 206 bfd_init(); 207 return 0; 208 } 209