196 lines
4.6 KiB
C
196 lines
4.6 KiB
C
|
// 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;
|
||
|
}
|