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