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 #ifndef _GNU_SOURCE 15 #define _GNU_SOURCE 16 #endif 17 #include <stdio.h> 18 #include <stdarg.h> 19 #include <stdint.h> 20 #include <stdlib.h> 21 #include <unistd.h> 22 #include <string.h> 23 #include <sys/stat.h> 24 #include <limits.h> 25 #include <bpf/libbpf.h> 26 27 #ifdef HAVE_LLVM_SUPPORT 28 #include <llvm-c/Core.h> 29 #include <llvm-c/Disassembler.h> 30 #include <llvm-c/Target.h> 31 #include <llvm-c/TargetMachine.h> 32 #endif 33 34 #ifdef HAVE_LIBBFD_SUPPORT 35 #include <bfd.h> 36 #include <dis-asm.h> 37 #include <tools/dis-asm-compat.h> 38 #endif 39 40 #include "json_writer.h" 41 #include "main.h" 42 43 static int oper_count; 44 45 #ifdef HAVE_LLVM_SUPPORT 46 #define DISASM_SPACER 47 48 typedef LLVMDisasmContextRef disasm_ctx_t; 49 50 static int printf_json(char *s) 51 { 52 s = strtok(s, " \t"); 53 jsonw_string_field(json_wtr, "operation", s); 54 55 jsonw_name(json_wtr, "operands"); 56 jsonw_start_array(json_wtr); 57 oper_count = 1; 58 59 while ((s = strtok(NULL, " \t,()")) != 0) { 60 jsonw_string(json_wtr, s); 61 oper_count++; 62 } 63 return 0; 64 } 65 66 /* This callback to set the ref_type is necessary to have the LLVM disassembler 67 * print PC-relative addresses instead of byte offsets for branch instruction 68 * targets. 69 */ 70 static const char * 71 symbol_lookup_callback(__maybe_unused void *disasm_info, 72 __maybe_unused uint64_t ref_value, 73 uint64_t *ref_type, __maybe_unused uint64_t ref_PC, 74 __maybe_unused const char **ref_name) 75 { 76 *ref_type = LLVMDisassembler_ReferenceType_InOut_None; 77 return NULL; 78 } 79 80 static int 81 init_context(disasm_ctx_t *ctx, const char *arch, 82 __maybe_unused const char *disassembler_options, 83 __maybe_unused unsigned char *image, __maybe_unused ssize_t len) 84 { 85 char *triple; 86 87 if (arch) 88 triple = LLVMNormalizeTargetTriple(arch); 89 else 90 triple = LLVMGetDefaultTargetTriple(); 91 if (!triple) { 92 p_err("Failed to retrieve triple"); 93 return -1; 94 } 95 *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback); 96 LLVMDisposeMessage(triple); 97 98 if (!*ctx) { 99 p_err("Failed to create disassembler"); 100 return -1; 101 } 102 103 return 0; 104 } 105 106 static void destroy_context(disasm_ctx_t *ctx) 107 { 108 LLVMDisposeMessage(*ctx); 109 } 110 111 static int 112 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc) 113 { 114 char buf[256]; 115 int count; 116 117 count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc, 118 buf, sizeof(buf)); 119 if (json_output) 120 printf_json(buf); 121 else 122 printf("%s", buf); 123 124 return count; 125 } 126 127 int disasm_init(void) 128 { 129 LLVMInitializeAllTargetInfos(); 130 LLVMInitializeAllTargetMCs(); 131 LLVMInitializeAllDisassemblers(); 132 return 0; 133 } 134 #endif /* HAVE_LLVM_SUPPORT */ 135 136 #ifdef HAVE_LIBBFD_SUPPORT 137 #define DISASM_SPACER "\t" 138 139 typedef struct { 140 struct disassemble_info *info; 141 disassembler_ftype disassemble; 142 bfd *bfdf; 143 } disasm_ctx_t; 144 145 static int get_exec_path(char *tpath, size_t size) 146 { 147 const char *path = "/proc/self/exe"; 148 ssize_t len; 149 150 len = readlink(path, tpath, size - 1); 151 if (len <= 0) 152 return -1; 153 154 tpath[len] = 0; 155 156 return 0; 157 } 158 159 static int printf_json(void *out, const char *fmt, va_list ap) 160 { 161 char *s; 162 int err; 163 164 err = vasprintf(&s, fmt, ap); 165 if (err < 0) 166 return -1; 167 168 if (!oper_count) { 169 int i; 170 171 /* Strip trailing spaces */ 172 i = strlen(s) - 1; 173 while (s[i] == ' ') 174 s[i--] = '\0'; 175 176 jsonw_string_field(json_wtr, "operation", s); 177 jsonw_name(json_wtr, "operands"); 178 jsonw_start_array(json_wtr); 179 oper_count++; 180 } else if (!strcmp(fmt, ",")) { 181 /* Skip */ 182 } else { 183 jsonw_string(json_wtr, s); 184 oper_count++; 185 } 186 free(s); 187 return 0; 188 } 189 190 static int fprintf_json(void *out, const char *fmt, ...) 191 { 192 va_list ap; 193 int r; 194 195 va_start(ap, fmt); 196 r = printf_json(out, fmt, ap); 197 va_end(ap); 198 199 return r; 200 } 201 202 static int fprintf_json_styled(void *out, 203 enum disassembler_style style __maybe_unused, 204 const char *fmt, ...) 205 { 206 va_list ap; 207 int r; 208 209 va_start(ap, fmt); 210 r = printf_json(out, fmt, ap); 211 va_end(ap); 212 213 return r; 214 } 215 216 static int init_context(disasm_ctx_t *ctx, const char *arch, 217 const char *disassembler_options, 218 unsigned char *image, ssize_t len) 219 { 220 struct disassemble_info *info; 221 char tpath[PATH_MAX]; 222 bfd *bfdf; 223 224 memset(tpath, 0, sizeof(tpath)); 225 if (get_exec_path(tpath, sizeof(tpath))) { 226 p_err("failed to create disassembler (get_exec_path)"); 227 return -1; 228 } 229 230 ctx->bfdf = bfd_openr(tpath, NULL); 231 if (!ctx->bfdf) { 232 p_err("failed to create disassembler (bfd_openr)"); 233 return -1; 234 } 235 if (!bfd_check_format(ctx->bfdf, bfd_object)) { 236 p_err("failed to create disassembler (bfd_check_format)"); 237 goto err_close; 238 } 239 bfdf = ctx->bfdf; 240 241 ctx->info = malloc(sizeof(struct disassemble_info)); 242 if (!ctx->info) { 243 p_err("mem alloc failed"); 244 goto err_close; 245 } 246 info = ctx->info; 247 248 if (json_output) 249 init_disassemble_info_compat(info, stdout, 250 (fprintf_ftype) fprintf_json, 251 fprintf_json_styled); 252 else 253 init_disassemble_info_compat(info, stdout, 254 (fprintf_ftype) fprintf, 255 fprintf_styled); 256 257 /* Update architecture info for offload. */ 258 if (arch) { 259 const bfd_arch_info_type *inf = bfd_scan_arch(arch); 260 261 if (inf) { 262 bfdf->arch_info = inf; 263 } else { 264 p_err("No libbfd support for %s", arch); 265 goto err_free; 266 } 267 } 268 269 info->arch = bfd_get_arch(bfdf); 270 info->mach = bfd_get_mach(bfdf); 271 if (disassembler_options) 272 info->disassembler_options = disassembler_options; 273 info->buffer = image; 274 info->buffer_length = len; 275 276 disassemble_init_for_target(info); 277 278 #ifdef DISASM_FOUR_ARGS_SIGNATURE 279 ctx->disassemble = disassembler(info->arch, 280 bfd_big_endian(bfdf), 281 info->mach, 282 bfdf); 283 #else 284 ctx->disassemble = disassembler(bfdf); 285 #endif 286 if (!ctx->disassemble) { 287 p_err("failed to create disassembler"); 288 goto err_free; 289 } 290 return 0; 291 292 err_free: 293 free(info); 294 err_close: 295 bfd_close(ctx->bfdf); 296 return -1; 297 } 298 299 static void destroy_context(disasm_ctx_t *ctx) 300 { 301 free(ctx->info); 302 bfd_close(ctx->bfdf); 303 } 304 305 static int 306 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image, 307 __maybe_unused ssize_t len, int pc) 308 { 309 return ctx->disassemble(pc, ctx->info); 310 } 311 312 int disasm_init(void) 313 { 314 bfd_init(); 315 return 0; 316 } 317 #endif /* HAVE_LIBBPFD_SUPPORT */ 318 319 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, 320 const char *arch, const char *disassembler_options, 321 const struct btf *btf, 322 const struct bpf_prog_linfo *prog_linfo, 323 __u64 func_ksym, unsigned int func_idx, 324 bool linum) 325 { 326 const struct bpf_line_info *linfo = NULL; 327 unsigned int nr_skip = 0; 328 int count, i, pc = 0; 329 disasm_ctx_t ctx; 330 331 if (!len) 332 return -1; 333 334 if (init_context(&ctx, arch, disassembler_options, image, len)) 335 return -1; 336 337 if (json_output) 338 jsonw_start_array(json_wtr); 339 do { 340 if (prog_linfo) { 341 linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, 342 func_ksym + pc, 343 func_idx, 344 nr_skip); 345 if (linfo) 346 nr_skip++; 347 } 348 349 if (json_output) { 350 jsonw_start_object(json_wtr); 351 oper_count = 0; 352 if (linfo) 353 btf_dump_linfo_json(btf, linfo, linum); 354 jsonw_name(json_wtr, "pc"); 355 jsonw_printf(json_wtr, "\"0x%x\"", pc); 356 } else { 357 if (linfo) 358 btf_dump_linfo_plain(btf, linfo, "; ", 359 linum); 360 printf("%4x:" DISASM_SPACER, pc); 361 } 362 363 count = disassemble_insn(&ctx, image, len, pc); 364 365 if (json_output) { 366 /* Operand array, was started in fprintf_json. Before 367 * that, make sure we have a _null_ value if no operand 368 * other than operation code was present. 369 */ 370 if (oper_count == 1) 371 jsonw_null(json_wtr); 372 jsonw_end_array(json_wtr); 373 } 374 375 if (opcodes) { 376 if (json_output) { 377 jsonw_name(json_wtr, "opcodes"); 378 jsonw_start_array(json_wtr); 379 for (i = 0; i < count; ++i) 380 jsonw_printf(json_wtr, "\"0x%02hhx\"", 381 (uint8_t)image[pc + i]); 382 jsonw_end_array(json_wtr); 383 } else { 384 printf("\n\t"); 385 for (i = 0; i < count; ++i) 386 printf("%02x ", 387 (uint8_t)image[pc + i]); 388 } 389 } 390 if (json_output) 391 jsonw_end_object(json_wtr); 392 else 393 printf("\n"); 394 395 pc += count; 396 } while (count > 0 && pc < len); 397 if (json_output) 398 jsonw_end_array(json_wtr); 399 400 destroy_context(&ctx); 401 402 return 0; 403 } 404