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