162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Based on: 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Minimal BPF JIT image disassembler 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for 862306a36Sopenharmony_ci * debugging or verification purposes. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net> 1162306a36Sopenharmony_ci * Licensed under the GNU General Public License, version 2.0 (GPLv2) 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#ifndef _GNU_SOURCE 1562306a36Sopenharmony_ci#define _GNU_SOURCE 1662306a36Sopenharmony_ci#endif 1762306a36Sopenharmony_ci#include <stdio.h> 1862306a36Sopenharmony_ci#include <stdarg.h> 1962306a36Sopenharmony_ci#include <stdint.h> 2062306a36Sopenharmony_ci#include <stdlib.h> 2162306a36Sopenharmony_ci#include <unistd.h> 2262306a36Sopenharmony_ci#include <string.h> 2362306a36Sopenharmony_ci#include <sys/stat.h> 2462306a36Sopenharmony_ci#include <limits.h> 2562306a36Sopenharmony_ci#include <bpf/libbpf.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#ifdef HAVE_LLVM_SUPPORT 2862306a36Sopenharmony_ci#include <llvm-c/Core.h> 2962306a36Sopenharmony_ci#include <llvm-c/Disassembler.h> 3062306a36Sopenharmony_ci#include <llvm-c/Target.h> 3162306a36Sopenharmony_ci#include <llvm-c/TargetMachine.h> 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#ifdef HAVE_LIBBFD_SUPPORT 3562306a36Sopenharmony_ci#include <bfd.h> 3662306a36Sopenharmony_ci#include <dis-asm.h> 3762306a36Sopenharmony_ci#include <tools/dis-asm-compat.h> 3862306a36Sopenharmony_ci#endif 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include "json_writer.h" 4162306a36Sopenharmony_ci#include "main.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int oper_count; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#ifdef HAVE_LLVM_SUPPORT 4662306a36Sopenharmony_ci#define DISASM_SPACER 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_citypedef LLVMDisasmContextRef disasm_ctx_t; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int printf_json(char *s) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci s = strtok(s, " \t"); 5362306a36Sopenharmony_ci jsonw_string_field(json_wtr, "operation", s); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci jsonw_name(json_wtr, "operands"); 5662306a36Sopenharmony_ci jsonw_start_array(json_wtr); 5762306a36Sopenharmony_ci oper_count = 1; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci while ((s = strtok(NULL, " \t,()")) != 0) { 6062306a36Sopenharmony_ci jsonw_string(json_wtr, s); 6162306a36Sopenharmony_ci oper_count++; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* This callback to set the ref_type is necessary to have the LLVM disassembler 6762306a36Sopenharmony_ci * print PC-relative addresses instead of byte offsets for branch instruction 6862306a36Sopenharmony_ci * targets. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic const char * 7162306a36Sopenharmony_cisymbol_lookup_callback(__maybe_unused void *disasm_info, 7262306a36Sopenharmony_ci __maybe_unused uint64_t ref_value, 7362306a36Sopenharmony_ci uint64_t *ref_type, __maybe_unused uint64_t ref_PC, 7462306a36Sopenharmony_ci __maybe_unused const char **ref_name) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci *ref_type = LLVMDisassembler_ReferenceType_InOut_None; 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int 8162306a36Sopenharmony_ciinit_context(disasm_ctx_t *ctx, const char *arch, 8262306a36Sopenharmony_ci __maybe_unused const char *disassembler_options, 8362306a36Sopenharmony_ci __maybe_unused unsigned char *image, __maybe_unused ssize_t len) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci char *triple; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (arch) 8862306a36Sopenharmony_ci triple = LLVMNormalizeTargetTriple(arch); 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci triple = LLVMGetDefaultTargetTriple(); 9162306a36Sopenharmony_ci if (!triple) { 9262306a36Sopenharmony_ci p_err("Failed to retrieve triple"); 9362306a36Sopenharmony_ci return -1; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback); 9662306a36Sopenharmony_ci LLVMDisposeMessage(triple); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!*ctx) { 9962306a36Sopenharmony_ci p_err("Failed to create disassembler"); 10062306a36Sopenharmony_ci return -1; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void destroy_context(disasm_ctx_t *ctx) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci LLVMDisposeMessage(*ctx); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int 11262306a36Sopenharmony_cidisassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci char buf[256]; 11562306a36Sopenharmony_ci int count; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc, 11862306a36Sopenharmony_ci buf, sizeof(buf)); 11962306a36Sopenharmony_ci if (json_output) 12062306a36Sopenharmony_ci printf_json(buf); 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci printf("%s", buf); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return count; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint disasm_init(void) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci LLVMInitializeAllTargetInfos(); 13062306a36Sopenharmony_ci LLVMInitializeAllTargetMCs(); 13162306a36Sopenharmony_ci LLVMInitializeAllDisassemblers(); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci#endif /* HAVE_LLVM_SUPPORT */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#ifdef HAVE_LIBBFD_SUPPORT 13762306a36Sopenharmony_ci#define DISASM_SPACER "\t" 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_citypedef struct { 14062306a36Sopenharmony_ci struct disassemble_info *info; 14162306a36Sopenharmony_ci disassembler_ftype disassemble; 14262306a36Sopenharmony_ci bfd *bfdf; 14362306a36Sopenharmony_ci} disasm_ctx_t; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int get_exec_path(char *tpath, size_t size) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci const char *path = "/proc/self/exe"; 14862306a36Sopenharmony_ci ssize_t len; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci len = readlink(path, tpath, size - 1); 15162306a36Sopenharmony_ci if (len <= 0) 15262306a36Sopenharmony_ci return -1; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci tpath[len] = 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int printf_json(void *out, const char *fmt, va_list ap) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci char *s; 16262306a36Sopenharmony_ci int err; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err = vasprintf(&s, fmt, ap); 16562306a36Sopenharmony_ci if (err < 0) 16662306a36Sopenharmony_ci return -1; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!oper_count) { 16962306a36Sopenharmony_ci int i; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Strip trailing spaces */ 17262306a36Sopenharmony_ci i = strlen(s) - 1; 17362306a36Sopenharmony_ci while (s[i] == ' ') 17462306a36Sopenharmony_ci s[i--] = '\0'; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci jsonw_string_field(json_wtr, "operation", s); 17762306a36Sopenharmony_ci jsonw_name(json_wtr, "operands"); 17862306a36Sopenharmony_ci jsonw_start_array(json_wtr); 17962306a36Sopenharmony_ci oper_count++; 18062306a36Sopenharmony_ci } else if (!strcmp(fmt, ",")) { 18162306a36Sopenharmony_ci /* Skip */ 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci jsonw_string(json_wtr, s); 18462306a36Sopenharmony_ci oper_count++; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci free(s); 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int fprintf_json(void *out, const char *fmt, ...) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci va_list ap; 19362306a36Sopenharmony_ci int r; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci va_start(ap, fmt); 19662306a36Sopenharmony_ci r = printf_json(out, fmt, ap); 19762306a36Sopenharmony_ci va_end(ap); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return r; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int fprintf_json_styled(void *out, 20362306a36Sopenharmony_ci enum disassembler_style style __maybe_unused, 20462306a36Sopenharmony_ci const char *fmt, ...) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci va_list ap; 20762306a36Sopenharmony_ci int r; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci va_start(ap, fmt); 21062306a36Sopenharmony_ci r = printf_json(out, fmt, ap); 21162306a36Sopenharmony_ci va_end(ap); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return r; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int init_context(disasm_ctx_t *ctx, const char *arch, 21762306a36Sopenharmony_ci const char *disassembler_options, 21862306a36Sopenharmony_ci unsigned char *image, ssize_t len) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct disassemble_info *info; 22162306a36Sopenharmony_ci char tpath[PATH_MAX]; 22262306a36Sopenharmony_ci bfd *bfdf; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci memset(tpath, 0, sizeof(tpath)); 22562306a36Sopenharmony_ci if (get_exec_path(tpath, sizeof(tpath))) { 22662306a36Sopenharmony_ci p_err("failed to create disassembler (get_exec_path)"); 22762306a36Sopenharmony_ci return -1; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ctx->bfdf = bfd_openr(tpath, NULL); 23162306a36Sopenharmony_ci if (!ctx->bfdf) { 23262306a36Sopenharmony_ci p_err("failed to create disassembler (bfd_openr)"); 23362306a36Sopenharmony_ci return -1; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci if (!bfd_check_format(ctx->bfdf, bfd_object)) { 23662306a36Sopenharmony_ci p_err("failed to create disassembler (bfd_check_format)"); 23762306a36Sopenharmony_ci goto err_close; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci bfdf = ctx->bfdf; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ctx->info = malloc(sizeof(struct disassemble_info)); 24262306a36Sopenharmony_ci if (!ctx->info) { 24362306a36Sopenharmony_ci p_err("mem alloc failed"); 24462306a36Sopenharmony_ci goto err_close; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci info = ctx->info; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (json_output) 24962306a36Sopenharmony_ci init_disassemble_info_compat(info, stdout, 25062306a36Sopenharmony_ci (fprintf_ftype) fprintf_json, 25162306a36Sopenharmony_ci fprintf_json_styled); 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci init_disassemble_info_compat(info, stdout, 25462306a36Sopenharmony_ci (fprintf_ftype) fprintf, 25562306a36Sopenharmony_ci fprintf_styled); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Update architecture info for offload. */ 25862306a36Sopenharmony_ci if (arch) { 25962306a36Sopenharmony_ci const bfd_arch_info_type *inf = bfd_scan_arch(arch); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (inf) { 26262306a36Sopenharmony_ci bfdf->arch_info = inf; 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci p_err("No libbfd support for %s", arch); 26562306a36Sopenharmony_ci goto err_free; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci info->arch = bfd_get_arch(bfdf); 27062306a36Sopenharmony_ci info->mach = bfd_get_mach(bfdf); 27162306a36Sopenharmony_ci if (disassembler_options) 27262306a36Sopenharmony_ci info->disassembler_options = disassembler_options; 27362306a36Sopenharmony_ci info->buffer = image; 27462306a36Sopenharmony_ci info->buffer_length = len; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci disassemble_init_for_target(info); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci#ifdef DISASM_FOUR_ARGS_SIGNATURE 27962306a36Sopenharmony_ci ctx->disassemble = disassembler(info->arch, 28062306a36Sopenharmony_ci bfd_big_endian(bfdf), 28162306a36Sopenharmony_ci info->mach, 28262306a36Sopenharmony_ci bfdf); 28362306a36Sopenharmony_ci#else 28462306a36Sopenharmony_ci ctx->disassemble = disassembler(bfdf); 28562306a36Sopenharmony_ci#endif 28662306a36Sopenharmony_ci if (!ctx->disassemble) { 28762306a36Sopenharmony_ci p_err("failed to create disassembler"); 28862306a36Sopenharmony_ci goto err_free; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cierr_free: 29362306a36Sopenharmony_ci free(info); 29462306a36Sopenharmony_cierr_close: 29562306a36Sopenharmony_ci bfd_close(ctx->bfdf); 29662306a36Sopenharmony_ci return -1; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void destroy_context(disasm_ctx_t *ctx) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci free(ctx->info); 30262306a36Sopenharmony_ci bfd_close(ctx->bfdf); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int 30662306a36Sopenharmony_cidisassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image, 30762306a36Sopenharmony_ci __maybe_unused ssize_t len, int pc) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci return ctx->disassemble(pc, ctx->info); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciint disasm_init(void) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci bfd_init(); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci#endif /* HAVE_LIBBPFD_SUPPORT */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciint disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, 32062306a36Sopenharmony_ci const char *arch, const char *disassembler_options, 32162306a36Sopenharmony_ci const struct btf *btf, 32262306a36Sopenharmony_ci const struct bpf_prog_linfo *prog_linfo, 32362306a36Sopenharmony_ci __u64 func_ksym, unsigned int func_idx, 32462306a36Sopenharmony_ci bool linum) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci const struct bpf_line_info *linfo = NULL; 32762306a36Sopenharmony_ci unsigned int nr_skip = 0; 32862306a36Sopenharmony_ci int count, i, pc = 0; 32962306a36Sopenharmony_ci disasm_ctx_t ctx; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!len) 33262306a36Sopenharmony_ci return -1; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (init_context(&ctx, arch, disassembler_options, image, len)) 33562306a36Sopenharmony_ci return -1; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (json_output) 33862306a36Sopenharmony_ci jsonw_start_array(json_wtr); 33962306a36Sopenharmony_ci do { 34062306a36Sopenharmony_ci if (prog_linfo) { 34162306a36Sopenharmony_ci linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, 34262306a36Sopenharmony_ci func_ksym + pc, 34362306a36Sopenharmony_ci func_idx, 34462306a36Sopenharmony_ci nr_skip); 34562306a36Sopenharmony_ci if (linfo) 34662306a36Sopenharmony_ci nr_skip++; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (json_output) { 35062306a36Sopenharmony_ci jsonw_start_object(json_wtr); 35162306a36Sopenharmony_ci oper_count = 0; 35262306a36Sopenharmony_ci if (linfo) 35362306a36Sopenharmony_ci btf_dump_linfo_json(btf, linfo, linum); 35462306a36Sopenharmony_ci jsonw_name(json_wtr, "pc"); 35562306a36Sopenharmony_ci jsonw_printf(json_wtr, "\"0x%x\"", pc); 35662306a36Sopenharmony_ci } else { 35762306a36Sopenharmony_ci if (linfo) 35862306a36Sopenharmony_ci btf_dump_linfo_plain(btf, linfo, "; ", 35962306a36Sopenharmony_ci linum); 36062306a36Sopenharmony_ci printf("%4x:" DISASM_SPACER, pc); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci count = disassemble_insn(&ctx, image, len, pc); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (json_output) { 36662306a36Sopenharmony_ci /* Operand array, was started in fprintf_json. Before 36762306a36Sopenharmony_ci * that, make sure we have a _null_ value if no operand 36862306a36Sopenharmony_ci * other than operation code was present. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci if (oper_count == 1) 37162306a36Sopenharmony_ci jsonw_null(json_wtr); 37262306a36Sopenharmony_ci jsonw_end_array(json_wtr); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (opcodes) { 37662306a36Sopenharmony_ci if (json_output) { 37762306a36Sopenharmony_ci jsonw_name(json_wtr, "opcodes"); 37862306a36Sopenharmony_ci jsonw_start_array(json_wtr); 37962306a36Sopenharmony_ci for (i = 0; i < count; ++i) 38062306a36Sopenharmony_ci jsonw_printf(json_wtr, "\"0x%02hhx\"", 38162306a36Sopenharmony_ci (uint8_t)image[pc + i]); 38262306a36Sopenharmony_ci jsonw_end_array(json_wtr); 38362306a36Sopenharmony_ci } else { 38462306a36Sopenharmony_ci printf("\n\t"); 38562306a36Sopenharmony_ci for (i = 0; i < count; ++i) 38662306a36Sopenharmony_ci printf("%02x ", 38762306a36Sopenharmony_ci (uint8_t)image[pc + i]); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci if (json_output) 39162306a36Sopenharmony_ci jsonw_end_object(json_wtr); 39262306a36Sopenharmony_ci else 39362306a36Sopenharmony_ci printf("\n"); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci pc += count; 39662306a36Sopenharmony_ci } while (count > 0 && pc < len); 39762306a36Sopenharmony_ci if (json_output) 39862306a36Sopenharmony_ci jsonw_end_array(json_wtr); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci destroy_context(&ctx); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 404