Commit b5c9bcde authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

perf capstone: Support for dlopen-ing libcapstone.so



If perf is built with LIBCAPSTONE_DLOPEN=1, support dlopen-ing
libcapstone.so and then calling the necessary functions by looking them
up using dlsym.

The types come from capstone.h which means the libcapstone feature check
needs to pass, and NO_CAPSTONE=1 hasn't been defined. This will cause
the definition of HAVE_LIBCAPSTONE_SUPPORT.

Earlier versions of this code tried to declare the necessary
capstone.h constants and structs, but they weren't stable and caused
breakages across libcapstone releases.

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Bill Wendling <morbo@google.com>
Cc: Charlie Jenkins <charlie@rivosinc.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Justin Stitt <justinstitt@google.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 169343cc
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -1078,8 +1078,12 @@ ifndef NO_CAPSTONE
  $(call feature_check,libcapstone)
  ifeq ($(feature-libcapstone), 1)
    CFLAGS += -DHAVE_LIBCAPSTONE_SUPPORT $(LIBCAPSTONE_CFLAGS)
    LDFLAGS += $(LICAPSTONE_LDFLAGS)
    ifdef LIBCAPSTONE_DLOPEN
      CFLAGS += -DLIBCAPSTONE_DLOPEN
    else
      LDFLAGS += $(LIBCAPSTONE_LDFLAGS)
      EXTLIBS += -lcapstone
    endif
    $(call detected,CONFIG_LIBCAPSTONE)
  else
    msg := $(warning No libcapstone found, disables disasm engine support for 'perf script', please install libcapstone-dev/capstone-devel);
+2 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ make_no_libdw := NO_LIBDW=1
make_libunwind      := LIBUNWIND=1
make_no_backtrace   := NO_BACKTRACE=1
make_no_libcapstone := NO_CAPSTONE=1
make_libcapstone_dlopen := LIBCAPSTONE_DLOPEN=1
make_no_libnuma     := NO_LIBNUMA=1
make_no_libbionic   := NO_LIBBIONIC=1
make_no_libbpf	    := NO_LIBBPF=1
@@ -159,6 +160,7 @@ run += make_libunwind
run += make_no_libdw_dwarf_unwind
run += make_no_backtrace
run += make_no_libcapstone
run += make_libcapstone_dlopen
run += make_no_libnuma
run += make_no_libbionic
run += make_no_libbpf
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ perf-util-y += block-info.o
perf-util-y += block-range.o
perf-util-y += build-id.o
perf-util-y += cacheline.o
perf-util-y += capstone.o
perf-util-$(CONFIG_LIBCAPSTONE) += capstone.o
perf-util-y += config.o
perf-util-y += copyfile.o
perf-util-y += ctype.o
+137 −39
Original line number Diff line number Diff line
@@ -11,20 +11,137 @@
#include "print_insn.h"
#include "symbol.h"
#include "thread.h"
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <string.h>

#ifdef HAVE_LIBCAPSTONE_SUPPORT
#include <capstone/capstone.h>

#ifdef LIBCAPSTONE_DLOPEN
static void *perf_cs_dll_handle(void)
{
	static bool dll_handle_init;
	static void *dll_handle;

	if (!dll_handle_init) {
		dll_handle_init = true;
		dll_handle = dlopen("libcapstone.so", RTLD_LAZY);
		if (!dll_handle)
			pr_debug("dlopen failed for libcapstone.so\n");
	}
	return dll_handle;
}
#endif

static enum cs_err perf_cs_open(enum cs_arch arch, enum cs_mode mode, csh *handle)
{
#ifndef LIBCAPSTONE_DLOPEN
	return cs_open(arch, mode, handle);
#else
	static bool fn_init;
	static enum cs_err (*fn)(enum cs_arch arch, enum cs_mode mode, csh *handle);

	if (!fn_init) {
		fn = dlsym(perf_cs_dll_handle(), "cs_open");
		if (!fn)
			pr_debug("dlsym failed for cs_open\n");
		fn_init = true;
	}
	if (!fn)
		return CS_ERR_HANDLE;
	return fn(arch, mode, handle);
#endif
}

static enum cs_err perf_cs_option(csh handle, enum cs_opt_type type, size_t value)
{
#ifndef LIBCAPSTONE_DLOPEN
	return cs_option(handle, type, value);
#else
	static bool fn_init;
	static enum cs_err (*fn)(csh handle, enum cs_opt_type type, size_t value);

	if (!fn_init) {
		fn = dlsym(perf_cs_dll_handle(), "cs_option");
		if (!fn)
			pr_debug("dlsym failed for cs_option\n");
		fn_init = true;
	}
	if (!fn)
		return CS_ERR_HANDLE;
	return fn(handle, type, value);
#endif
}

static size_t perf_cs_disasm(csh handle, const uint8_t *code, size_t code_size,
			uint64_t address, size_t count, struct cs_insn **insn)
{
#ifndef LIBCAPSTONE_DLOPEN
	return cs_disasm(handle, code, code_size, address, count, insn);
#else
	static bool fn_init;
	static enum cs_err (*fn)(csh handle, const uint8_t *code, size_t code_size,
				 uint64_t address, size_t count, struct cs_insn **insn);

	if (!fn_init) {
		fn = dlsym(perf_cs_dll_handle(), "cs_disasm");
		if (!fn)
			pr_debug("dlsym failed for cs_disasm\n");
		fn_init = true;
	}
	if (!fn)
		return CS_ERR_HANDLE;
	return fn(handle, code, code_size, address, count, insn);
#endif
}

static void perf_cs_free(struct cs_insn *insn, size_t count)
{
#ifndef LIBCAPSTONE_DLOPEN
	cs_free(insn, count);
#else
	static bool fn_init;
	static void (*fn)(struct cs_insn *insn, size_t count);

	if (!fn_init) {
		fn = dlsym(perf_cs_dll_handle(), "cs_free");
		if (!fn)
			pr_debug("dlsym failed for cs_free\n");
		fn_init = true;
	}
	if (!fn)
		return;
	fn(insn, count);
#endif
}

static enum cs_err perf_cs_close(csh *handle)
{
#ifndef LIBCAPSTONE_DLOPEN
	return cs_close(handle);
#else
	static bool fn_init;
	static enum cs_err (*fn)(csh *handle);

	if (!fn_init) {
		fn = dlsym(perf_cs_dll_handle(), "cs_close");
		if (!fn)
			pr_debug("dlsym failed for cs_close\n");
		fn_init = true;
	}
	if (!fn)
		return CS_ERR_HANDLE;
	return fn(handle);
#endif
}

#ifdef HAVE_LIBCAPSTONE_SUPPORT
static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
			 bool disassembler_style)
{
	cs_arch arch;
	cs_mode mode;
	enum cs_arch arch;
	enum cs_mode mode;

	if (machine__is(machine, "x86_64") && is64) {
		arch = CS_ARCH_X86;
@@ -45,7 +162,7 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
		return -1;
	}

	if (cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
	if (perf_cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
		pr_warning_once("cs_open failed\n");
		return -1;
	}
@@ -57,27 +174,25 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
		 * is set via annotation args
		 */
		if (disassembler_style)
			cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
			perf_cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
		/*
		 * Resolving address operands to symbols is implemented
		 * on x86 by investigating instruction details.
		 */
		cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
		perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
	}

	return 0;
}
#endif

#ifdef HAVE_LIBCAPSTONE_SUPPORT
static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
static size_t print_insn_x86(struct thread *thread, u8 cpumode, struct cs_insn *insn,
			     int print_opts, FILE *fp)
{
	struct addr_location al;
	size_t printed = 0;

	if (insn->detail && insn->detail->x86.op_count == 1) {
		cs_x86_op *op = &insn->detail->x86.operands[0];
		struct cs_x86_op *op = &insn->detail->x86.operands[0];

		addr_location__init(&al);
		if (op->type == X86_OP_IMM &&
@@ -95,7 +210,6 @@ static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
	printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
	return printed;
}
#endif


ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
@@ -106,9 +220,8 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
				   uint64_t ip __maybe_unused, int *lenp __maybe_unused,
				   int print_opts __maybe_unused, FILE *fp __maybe_unused)
{
#ifdef HAVE_LIBCAPSTONE_SUPPORT
	size_t printed;
	cs_insn *insn;
	struct cs_insn *insn;
	csh cs_handle;
	size_t count;
	int ret;
@@ -118,7 +231,7 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
	if (ret < 0)
		return ret;

	count = cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
	count = perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
	if (count > 0) {
		if (machine__normalized_is(machine, "x86"))
			printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);
@@ -126,20 +239,16 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
			printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
		if (lenp)
			*lenp = insn->size;
		cs_free(insn, count);
		perf_cs_free(insn, count);
	} else {
		printed = -1;
	}

	cs_close(&cs_handle);
	perf_cs_close(&cs_handle);
	return printed;
#else
	return -1;
#endif
}

#ifdef HAVE_LIBCAPSTONE_SUPPORT
static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
				  struct annotate_args *args, u64 addr)
{
	int i;
@@ -154,7 +263,7 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
		return;

	for (i = 0; i < insn->detail->x86.op_count; i++) {
		cs_x86_op *op = &insn->detail->x86.operands[i];
		struct cs_x86_op *op = &insn->detail->x86.operands[i];
		u64 orig_addr;

		if (op->type != X86_OP_MEM)
@@ -195,9 +304,7 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
		break;
	}
}
#endif

#ifdef HAVE_LIBCAPSTONE_SUPPORT
struct find_file_offset_data {
	u64 ip;
	u64 offset;
@@ -214,13 +321,11 @@ static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
	}
	return 0;
}
#endif

int symbol__disassemble_capstone(const char *filename __maybe_unused,
				 struct symbol *sym __maybe_unused,
				 struct annotate_args *args __maybe_unused)
{
#ifdef HAVE_LIBCAPSTONE_SUPPORT
	struct annotation *notes = symbol__annotation(sym);
	struct map *map = args->ms->map;
	struct dso *dso = map__dso(map);
@@ -235,7 +340,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
	const u8 *buf;
	u64 buf_len;
	csh handle;
	cs_insn *insn = NULL;
	struct cs_insn *insn = NULL;
	char disasm_buf[512];
	struct disasm_line *dl;
	bool disassembler_style = false;
@@ -274,7 +379,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,

	needs_cs_close = true;

	free_count = count = cs_disasm(handle, buf, buf_len, start, buf_len, &insn);
	free_count = count = perf_cs_disasm(handle, buf, buf_len, start, buf_len, &insn);
	for (i = 0, offset = 0; i < count; i++) {
		int printed;

@@ -313,9 +418,9 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,

out:
	if (needs_cs_close) {
		cs_close(&handle);
		perf_cs_close(&handle);
		if (free_count > 0)
			cs_free(insn, free_count);
			perf_cs_free(insn, free_count);
	}
	free(code_buf);
	return count < 0 ? count : 0;
@@ -335,16 +440,12 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
	}
	count = -1;
	goto out;
#else
	return -1;
#endif
}

int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
					 struct symbol *sym __maybe_unused,
					 struct annotate_args *args __maybe_unused)
{
#ifdef HAVE_LIBCAPSTONE_SUPPORT
	struct annotation *notes = symbol__annotation(sym);
	struct map *map = args->ms->map;
	struct dso *dso = map__dso(map);
@@ -458,7 +559,7 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,

out:
	if (needs_cs_close)
		cs_close(&handle);
		perf_cs_close(&handle);
	free(buf);
	return count < 0 ? count : 0;

@@ -467,7 +568,4 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
		close(fd);
	count = -1;
	goto out;
#else
	return -1;
#endif
}
+33 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/compiler.h>
#include <linux/types.h>

struct annotate_args;
@@ -13,6 +14,7 @@ struct machine;
struct symbol;
struct thread;

#ifdef HAVE_LIBCAPSTONE_SUPPORT
ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpumode,
				   bool is64bit, const uint8_t *code, size_t code_size,
				   uint64_t ip, int *lenp, int print_opts, FILE *fp);
@@ -21,4 +23,35 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
int symbol__disassemble_capstone_powerpc(const char *filename, struct symbol *sym,
					 struct annotate_args *args);

#else /* !HAVE_LIBCAPSTONE_SUPPORT */
static inline ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
						 struct thread *thread __maybe_unused,
						 u8 cpumode __maybe_unused,
						 bool is64bit __maybe_unused,
						 const uint8_t *code __maybe_unused,
						 size_t code_size __maybe_unused,
						 uint64_t ip __maybe_unused,
						 int *lenp __maybe_unused,
						 int print_opts __maybe_unused,
						 FILE *fp __maybe_unused)
{
	return -1;
}

static inline int symbol__disassemble_capstone(const char *filename __maybe_unused,
					       struct symbol *sym __maybe_unused,
					       struct annotate_args *args __maybe_unused)
{
	return -1;
}

static inline int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
						       struct symbol *sym __maybe_unused,
						       struct annotate_args *args __maybe_unused)
{
	return -1;
}

#endif /* HAVE_LIBCAPSTONE_SUPPORT */

#endif /* __PERF_CAPSTONE_H */