|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  |  | 
|  | #include "util/annotate.h" | 
|  | #include "util/disasm_bpf.h" | 
|  | #include "util/symbol.h" | 
|  | #include <linux/zalloc.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) | 
|  | #define PACKAGE "perf" | 
|  | #include <bfd.h> | 
|  | #include <bpf/bpf.h> | 
|  | #include <bpf/btf.h> | 
|  | #include <bpf/libbpf.h> | 
|  | #include <dis-asm.h> | 
|  | #include <errno.h> | 
|  | #include <linux/btf.h> | 
|  | #include <tools/dis-asm-compat.h> | 
|  |  | 
|  | #include "util/bpf-event.h" | 
|  | #include "util/bpf-utils.h" | 
|  | #include "util/debug.h" | 
|  | #include "util/dso.h" | 
|  | #include "util/map.h" | 
|  | #include "util/env.h" | 
|  | #include "util/util.h" | 
|  |  | 
|  | int symbol__disassemble_bpf(struct symbol *sym, struct annotate_args *args) | 
|  | { | 
|  | struct annotation *notes = symbol__annotation(sym); | 
|  | struct bpf_prog_linfo *prog_linfo = NULL; | 
|  | struct bpf_prog_info_node *info_node; | 
|  | int len = sym->end - sym->start; | 
|  | disassembler_ftype disassemble; | 
|  | struct map *map = args->ms.map; | 
|  | struct perf_bpil *info_linear; | 
|  | struct disassemble_info info; | 
|  | struct dso *dso = map__dso(map); | 
|  | int pc = 0, count, sub_id; | 
|  | struct btf *btf = NULL; | 
|  | char tpath[PATH_MAX]; | 
|  | size_t buf_size; | 
|  | int nr_skip = 0; | 
|  | char *buf; | 
|  | bfd *bfdf; | 
|  | int ret; | 
|  | FILE *s; | 
|  |  | 
|  | if (dso__binary_type(dso) != DSO_BINARY_TYPE__BPF_PROG_INFO) | 
|  | return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE; | 
|  |  | 
|  | pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__, | 
|  | sym->name, sym->start, sym->end - sym->start); | 
|  |  | 
|  | memset(tpath, 0, sizeof(tpath)); | 
|  | perf_exe(tpath, sizeof(tpath)); | 
|  |  | 
|  | bfdf = bfd_openr(tpath, NULL); | 
|  | if (bfdf == NULL) | 
|  | abort(); | 
|  |  | 
|  | if (!bfd_check_format(bfdf, bfd_object)) | 
|  | abort(); | 
|  |  | 
|  | s = open_memstream(&buf, &buf_size); | 
|  | if (!s) { | 
|  | ret = errno; | 
|  | goto out; | 
|  | } | 
|  | init_disassemble_info_compat(&info, s, | 
|  | (fprintf_ftype) fprintf, | 
|  | fprintf_styled); | 
|  | info.arch = bfd_get_arch(bfdf); | 
|  | info.mach = bfd_get_mach(bfdf); | 
|  |  | 
|  | info_node = perf_env__find_bpf_prog_info(dso__bpf_prog(dso)->env, | 
|  | dso__bpf_prog(dso)->id); | 
|  | if (!info_node) { | 
|  | ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; | 
|  | goto out; | 
|  | } | 
|  | info_linear = info_node->info_linear; | 
|  | sub_id = dso__bpf_prog(dso)->sub_id; | 
|  |  | 
|  | info.buffer = (void *)(uintptr_t)(info_linear->info.jited_prog_insns); | 
|  | info.buffer_length = info_linear->info.jited_prog_len; | 
|  |  | 
|  | if (info_linear->info.nr_line_info) | 
|  | prog_linfo = bpf_prog_linfo__new(&info_linear->info); | 
|  |  | 
|  | if (info_linear->info.btf_id) { | 
|  | struct btf_node *node; | 
|  |  | 
|  | node = perf_env__find_btf(dso__bpf_prog(dso)->env, | 
|  | info_linear->info.btf_id); | 
|  | if (node) | 
|  | btf = btf__new((__u8 *)(node->data), | 
|  | node->data_size); | 
|  | } | 
|  |  | 
|  | disassemble_init_for_target(&info); | 
|  |  | 
|  | #ifdef DISASM_FOUR_ARGS_SIGNATURE | 
|  | disassemble = disassembler(info.arch, | 
|  | bfd_big_endian(bfdf), | 
|  | info.mach, | 
|  | bfdf); | 
|  | #else | 
|  | disassemble = disassembler(bfdf); | 
|  | #endif | 
|  | if (disassemble == NULL) | 
|  | abort(); | 
|  |  | 
|  | fflush(s); | 
|  | do { | 
|  | const struct bpf_line_info *linfo = NULL; | 
|  | struct disasm_line *dl; | 
|  | size_t prev_buf_size; | 
|  | const char *srcline; | 
|  | u64 addr; | 
|  |  | 
|  | addr = pc + ((u64 *)(uintptr_t)(info_linear->info.jited_ksyms))[sub_id]; | 
|  | count = disassemble(pc, &info); | 
|  |  | 
|  | if (prog_linfo) | 
|  | linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, | 
|  | addr, sub_id, | 
|  | nr_skip); | 
|  |  | 
|  | if (linfo && btf) { | 
|  | srcline = btf__name_by_offset(btf, linfo->line_off); | 
|  | nr_skip++; | 
|  | } else | 
|  | srcline = NULL; | 
|  |  | 
|  | fprintf(s, "\n"); | 
|  | prev_buf_size = buf_size; | 
|  | fflush(s); | 
|  |  | 
|  | if (!annotate_opts.hide_src_code && srcline) { | 
|  | args->offset = -1; | 
|  | args->line = strdup(srcline); | 
|  | args->line_nr = 0; | 
|  | args->fileloc = NULL; | 
|  | args->ms.sym  = sym; | 
|  | dl = disasm_line__new(args); | 
|  | if (dl) { | 
|  | annotation_line__add(&dl->al, | 
|  | ¬es->src->source); | 
|  | } | 
|  | } | 
|  |  | 
|  | args->offset = pc; | 
|  | args->line = buf + prev_buf_size; | 
|  | args->line_nr = 0; | 
|  | args->fileloc = NULL; | 
|  | args->ms.sym  = sym; | 
|  | dl = disasm_line__new(args); | 
|  | if (dl) | 
|  | annotation_line__add(&dl->al, ¬es->src->source); | 
|  |  | 
|  | pc += count; | 
|  | } while (count > 0 && pc < len); | 
|  |  | 
|  | ret = 0; | 
|  | out: | 
|  | free(prog_linfo); | 
|  | btf__free(btf); | 
|  | fclose(s); | 
|  | bfd_close(bfdf); | 
|  | return ret; | 
|  | } | 
|  | #else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) | 
|  | int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, struct annotate_args *args __maybe_unused) | 
|  | { | 
|  | return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; | 
|  | } | 
|  | #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) | 
|  |  | 
|  | int symbol__disassemble_bpf_image(struct symbol *sym, struct annotate_args *args) | 
|  | { | 
|  | struct annotation *notes = symbol__annotation(sym); | 
|  | struct disasm_line *dl; | 
|  |  | 
|  | args->offset = -1; | 
|  | args->line = strdup("to be implemented"); | 
|  | args->line_nr = 0; | 
|  | args->fileloc = NULL; | 
|  | dl = disasm_line__new(args); | 
|  | if (dl) | 
|  | annotation_line__add(&dl->al, ¬es->src->source); | 
|  |  | 
|  | zfree(&args->line); | 
|  | return 0; | 
|  | } |