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