| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
- /*
- * Based on:
- *
- * Minimal BPF JIT image disassembler
- *
- * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
- * debugging or verification purposes.
- *
- * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
- * Licensed under the GNU General Public License, version 2.0 (GPLv2)
- */
- #ifndef _GNU_SOURCE
- #define _GNU_SOURCE
- #endif
- #include <stdio.h>
- #include <stdarg.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <limits.h>
- #include <bpf/libbpf.h>
- #ifdef HAVE_LLVM_SUPPORT
- #include <llvm-c/Core.h>
- #include <llvm-c/Disassembler.h>
- #include <llvm-c/Target.h>
- #include <llvm-c/TargetMachine.h>
- #endif
- #ifdef HAVE_LIBBFD_SUPPORT
- #include <bfd.h>
- #include <dis-asm.h>
- #include <tools/dis-asm-compat.h>
- #endif
- #include "json_writer.h"
- #include "main.h"
- static int oper_count;
- #ifdef HAVE_LLVM_SUPPORT
- #define DISASM_SPACER
- typedef LLVMDisasmContextRef disasm_ctx_t;
- static int printf_json(char *s)
- {
- s = strtok(s, " \t");
- jsonw_string_field(json_wtr, "operation", s);
- jsonw_name(json_wtr, "operands");
- jsonw_start_array(json_wtr);
- oper_count = 1;
- while ((s = strtok(NULL, " \t,()")) != 0) {
- jsonw_string(json_wtr, s);
- oper_count++;
- }
- return 0;
- }
- /* This callback to set the ref_type is necessary to have the LLVM disassembler
- * print PC-relative addresses instead of byte offsets for branch instruction
- * targets.
- */
- static const char *
- symbol_lookup_callback(__maybe_unused void *disasm_info,
- __maybe_unused uint64_t ref_value,
- uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
- __maybe_unused const char **ref_name)
- {
- *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
- return NULL;
- }
- static int
- init_context(disasm_ctx_t *ctx, const char *arch,
- __maybe_unused const char *disassembler_options,
- __maybe_unused unsigned char *image, __maybe_unused ssize_t len,
- __maybe_unused __u64 func_ksym)
- {
- char *triple;
- if (arch)
- triple = LLVMNormalizeTargetTriple(arch);
- else
- triple = LLVMGetDefaultTargetTriple();
- if (!triple) {
- p_err("Failed to retrieve triple");
- return -1;
- }
- *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
- LLVMDisposeMessage(triple);
- if (!*ctx) {
- p_err("Failed to create disassembler");
- return -1;
- }
- return 0;
- }
- static void destroy_context(disasm_ctx_t *ctx)
- {
- LLVMDisposeMessage(*ctx);
- }
- static int
- disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
- __u64 func_ksym)
- {
- char buf[256];
- int count;
- count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
- buf, sizeof(buf));
- if (json_output)
- printf_json(buf);
- else
- printf("%s", buf);
- return count;
- }
- int disasm_init(void)
- {
- LLVMInitializeAllTargetInfos();
- LLVMInitializeAllTargetMCs();
- LLVMInitializeAllDisassemblers();
- return 0;
- }
- #endif /* HAVE_LLVM_SUPPORT */
- #ifdef HAVE_LIBBFD_SUPPORT
- #define DISASM_SPACER "\t"
- struct disasm_info {
- struct disassemble_info info;
- __u64 func_ksym;
- };
- static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
- {
- struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
- addr += dinfo->func_ksym;
- generic_print_address(addr, info);
- }
- typedef struct {
- struct disasm_info *info;
- disassembler_ftype disassemble;
- bfd *bfdf;
- } disasm_ctx_t;
- static int get_exec_path(char *tpath, size_t size)
- {
- const char *path = "/proc/self/exe";
- ssize_t len;
- len = readlink(path, tpath, size - 1);
- if (len <= 0)
- return -1;
- tpath[len] = 0;
- return 0;
- }
- static int printf_json(void *out, const char *fmt, va_list ap)
- {
- char *s;
- int err;
- err = vasprintf(&s, fmt, ap);
- if (err < 0)
- return -1;
- if (!oper_count) {
- int i;
- /* Strip trailing spaces */
- i = strlen(s) - 1;
- while (s[i] == ' ')
- s[i--] = '\0';
- jsonw_string_field(json_wtr, "operation", s);
- jsonw_name(json_wtr, "operands");
- jsonw_start_array(json_wtr);
- oper_count++;
- } else if (!strcmp(fmt, ",")) {
- /* Skip */
- } else {
- jsonw_string(json_wtr, s);
- oper_count++;
- }
- free(s);
- return 0;
- }
- static int fprintf_json(void *out, const char *fmt, ...)
- {
- va_list ap;
- int r;
- va_start(ap, fmt);
- r = printf_json(out, fmt, ap);
- va_end(ap);
- return r;
- }
- static int fprintf_json_styled(void *out,
- enum disassembler_style style __maybe_unused,
- const char *fmt, ...)
- {
- va_list ap;
- int r;
- va_start(ap, fmt);
- r = printf_json(out, fmt, ap);
- va_end(ap);
- return r;
- }
- static int init_context(disasm_ctx_t *ctx, const char *arch,
- const char *disassembler_options,
- unsigned char *image, ssize_t len, __u64 func_ksym)
- {
- struct disassemble_info *info;
- char tpath[PATH_MAX];
- bfd *bfdf;
- memset(tpath, 0, sizeof(tpath));
- if (get_exec_path(tpath, sizeof(tpath))) {
- p_err("failed to create disassembler (get_exec_path)");
- return -1;
- }
- ctx->bfdf = bfd_openr(tpath, NULL);
- if (!ctx->bfdf) {
- p_err("failed to create disassembler (bfd_openr)");
- return -1;
- }
- if (!bfd_check_format(ctx->bfdf, bfd_object)) {
- p_err("failed to create disassembler (bfd_check_format)");
- goto err_close;
- }
- bfdf = ctx->bfdf;
- ctx->info = malloc(sizeof(struct disasm_info));
- if (!ctx->info) {
- p_err("mem alloc failed");
- goto err_close;
- }
- ctx->info->func_ksym = func_ksym;
- info = &ctx->info->info;
- if (json_output)
- init_disassemble_info_compat(info, stdout,
- (fprintf_ftype) fprintf_json,
- fprintf_json_styled);
- else
- init_disassemble_info_compat(info, stdout,
- (fprintf_ftype) fprintf,
- fprintf_styled);
- /* Update architecture info for offload. */
- if (arch) {
- const bfd_arch_info_type *inf = bfd_scan_arch(arch);
- if (inf) {
- bfdf->arch_info = inf;
- } else {
- p_err("No libbfd support for %s", arch);
- goto err_free;
- }
- }
- info->arch = bfd_get_arch(bfdf);
- info->mach = bfd_get_mach(bfdf);
- if (disassembler_options)
- info->disassembler_options = disassembler_options;
- info->buffer = image;
- info->buffer_length = len;
- info->print_address_func = disasm_print_addr;
- disassemble_init_for_target(info);
- #ifdef DISASM_FOUR_ARGS_SIGNATURE
- ctx->disassemble = disassembler(info->arch,
- bfd_big_endian(bfdf),
- info->mach,
- bfdf);
- #else
- ctx->disassemble = disassembler(bfdf);
- #endif
- if (!ctx->disassemble) {
- p_err("failed to create disassembler");
- goto err_free;
- }
- return 0;
- err_free:
- free(info);
- err_close:
- bfd_close(ctx->bfdf);
- return -1;
- }
- static void destroy_context(disasm_ctx_t *ctx)
- {
- free(ctx->info);
- bfd_close(ctx->bfdf);
- }
- static int
- disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
- __maybe_unused ssize_t len, int pc,
- __maybe_unused __u64 func_ksym)
- {
- return ctx->disassemble(pc, &ctx->info->info);
- }
- int disasm_init(void)
- {
- bfd_init();
- return 0;
- }
- #endif /* HAVE_LIBBPFD_SUPPORT */
- int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
- const char *arch, const char *disassembler_options,
- const struct btf *btf,
- const struct bpf_prog_linfo *prog_linfo,
- __u64 func_ksym, unsigned int func_idx,
- bool linum)
- {
- const struct bpf_line_info *linfo = NULL;
- unsigned int nr_skip = 0;
- int count, i;
- unsigned int pc = 0;
- disasm_ctx_t ctx;
- if (!len)
- return -1;
- if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
- return -1;
- if (json_output)
- jsonw_start_array(json_wtr);
- do {
- if (prog_linfo) {
- linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
- func_ksym + pc,
- func_idx,
- nr_skip);
- if (linfo)
- nr_skip++;
- }
- if (json_output) {
- jsonw_start_object(json_wtr);
- oper_count = 0;
- if (linfo)
- btf_dump_linfo_json(btf, linfo, linum);
- jsonw_name(json_wtr, "pc");
- jsonw_printf(json_wtr, "\"0x%x\"", pc);
- } else {
- if (linfo)
- btf_dump_linfo_plain(btf, linfo, "; ",
- linum);
- printf("%4x:" DISASM_SPACER, pc);
- }
- count = disassemble_insn(&ctx, image, len, pc, func_ksym);
- if (json_output) {
- /* Operand array, was started in fprintf_json. Before
- * that, make sure we have a _null_ value if no operand
- * other than operation code was present.
- */
- if (oper_count == 1)
- jsonw_null(json_wtr);
- jsonw_end_array(json_wtr);
- }
- if (opcodes) {
- if (json_output) {
- jsonw_name(json_wtr, "opcodes");
- jsonw_start_array(json_wtr);
- for (i = 0; i < count; ++i)
- jsonw_printf(json_wtr, "\"0x%02hhx\"",
- (uint8_t)image[pc + i]);
- jsonw_end_array(json_wtr);
- } else {
- printf("\n\t");
- for (i = 0; i < count; ++i)
- printf("%02x ",
- (uint8_t)image[pc + i]);
- }
- }
- if (json_output)
- jsonw_end_object(json_wtr);
- else
- printf("\n");
- pc += count;
- } while (count > 0 && pc < len);
- if (json_output)
- jsonw_end_array(json_wtr);
- destroy_context(&ctx);
- return 0;
- }
|