| // 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; |
| } |