From nobody Mon Feb 9 03:11:04 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C7A2C389477 for ; Fri, 30 Jan 2026 23:34:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769816087; cv=none; b=pP6ADtyUM/PGZJ/BzyPEnM9c9Y1uxWVwIXOWyZB43P14sPpsLqS/IzU66DL4N9Vhdq+pB08PUd6ZvftwXMlLdzk5b3SrDJNS1F/WYdIrB0Xx78snqR/OkusHnwpXQ7qOZuuaMQY7zV7LCjM91GI/RFvefXmeF9+fDWEAHi5WDWI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769816087; c=relaxed/simple; bh=eP+yFNqsM0F+bYwBExXX2CJyNNwIZLp4DSCLVeZFhdU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=kRxxExdy4UuQF+WQSPqM+M4QtPinpiWGhdCfJnd2InyfkBzUaG/uAz9SeXOgNx8a7yb75skEeDhQEV9QGpev/ZEUQJbNIq4ToS47kXBvUHXs7O49e4hUN/rzm66+FOu/hmMDViC5E8G6/6d3xbwFT4UVxjaaQkPBict5+F/NiS0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=GROFaqsX; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="GROFaqsX" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2b795cff830so725525eec.1 for ; Fri, 30 Jan 2026 15:34:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769816085; x=1770420885; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=GQj2Z4SylGf/VCJDURPMv3UALI8c3Pg/YVazRrOkdHk=; b=GROFaqsXJGZKIP2T9r1LGu6Re4bcsRcJ7RnKhyH3Q36HUCS3NwmM5aPYNZz4FmTCXd BsqN1i1jKg1LJOksysINOReRLAKF+u5zhmBEjNJRUOqMvrcf/NKjhgSku/2gaY962lOv /7FwnJYG2QmGNPOGUgKNvHbWl/2UoOSXCZe1CepuOxSx7tMyK+vAILb/8jUGK4oinKp2 ae3IRNm3dFtVwkeSr6bhRiwsCRrdzYMMdPJkD2VrQvVSWugc3GTV9+kuSpMxJvqVUiLL 3hCTTJflzBmoHfR+RpkKhLFON28P2SRrRiJjPmYHkJ3AZKdfuIzDNdGxKaItZEoSUsZx eEyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769816085; x=1770420885; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=GQj2Z4SylGf/VCJDURPMv3UALI8c3Pg/YVazRrOkdHk=; b=t2Rehk1BMA7NPokN/tADdHw/ji4254jhNVElblbWzvbxg93iw0YizBaXci1ATJDFGe q4ifcUqSVirAvP8i/gH3LCde6HjdqhtmTNm18OVF4f3LsGdmvbUmyRnc04bzjZ9vXyV+ kslqOdjL72C3+ol9qdLb7//6AUslv3SEvr583LL4ULGTGAayu0UP+QXiCd1oGZeTULf4 5vPtqhO8Op0yGX0hw1zHOPPViMmH9RdpCe1FE1F0mKlex99LW7KHnRhK5AiSy+GHRQEj 8akfTUPfyp1m1eak07p8Pz+dSWuY0h/OaIvwpXRevpm6OslxBN/1YM7sfI4QKJoh0SOS MuTA== X-Forwarded-Encrypted: i=1; AJvYcCVu+BdypthVpoK1qedlKJEAPzqoOzJWU7bIxy6Mrqb7bqqkoHJ81JlGrpfFgqoxfe4vNGgbHitoE5zbO3Q=@vger.kernel.org X-Gm-Message-State: AOJu0YxChidoewTtJ6GWQzRI5UqBVPlGUiyuwtVl4mM4CDdAZ/amaleB c6Ek9jBaFQjFi6rJzPv7irqUvC20/oneLwgB4wNy4vdcD6ypWSjO57IRr3UNDqHnsrnp000Nu4N WEcgf+xMkGg== X-Received: from dybtv10.prod.google.com ([2002:a05:7300:f48a:b0:2b7:9b1b:2ed4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:7305:b0:2b7:a3a9:9c28 with SMTP id 5a478bee46e88-2b7c88d8c3bmr2541185eec.20.1769816084925; Fri, 30 Jan 2026 15:34:44 -0800 (PST) Date: Fri, 30 Jan 2026 15:34:37 -0800 In-Reply-To: <20260130233439.647447-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130233439.647447-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.rc1.225.gd81095ad13-goog Message-ID: <20260130233439.647447-2-irogers@google.com> Subject: [PATCH v10 1/3] perf capstone: Support for dlopen-ing libcapstone.so From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Namhyung Kim , Alexander Shishkin , Jiri Olsa , Adrian Hunter , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Charlie Jenkins , "Masami Hiramatsu (Google)" , James Clark , Collin Funk , Dmitry Vyukov , linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, llvm@lists.linux.dev Cc: Ian Rogers Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" If perf is built with LIBCAPSTONE_DLOPEN=3D1, 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=3D1 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: Ian Rogers --- tools/perf/Makefile.config | 8 +- tools/perf/tests/make | 2 + tools/perf/util/Build | 2 +- tools/perf/util/capstone.c | 176 +++++++++++++++++++++++++++++-------- tools/perf/util/capstone.h | 33 +++++++ 5 files changed, 179 insertions(+), 42 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 63ca9b2be663..e085d27f698a 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -1078,8 +1078,12 @@ ifndef NO_CAPSTONE $(call feature_check,libcapstone) ifeq ($(feature-libcapstone), 1) CFLAGS +=3D -DHAVE_LIBCAPSTONE_SUPPORT $(LIBCAPSTONE_CFLAGS) - LDFLAGS +=3D $(LICAPSTONE_LDFLAGS) - EXTLIBS +=3D -lcapstone + ifdef LIBCAPSTONE_DLOPEN + CFLAGS +=3D -DLIBCAPSTONE_DLOPEN + else + LDFLAGS +=3D $(LIBCAPSTONE_LDFLAGS) + EXTLIBS +=3D -lcapstone + endif $(call detected,CONFIG_LIBCAPSTONE) else msg :=3D $(warning No libcapstone found, disables disasm engine suppor= t for 'perf script', please install libcapstone-dev/capstone-devel); diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 767ad9e147a8..f15096074d57 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -85,6 +85,7 @@ make_no_libdw :=3D NO_LIBDW=3D1 make_libunwind :=3D LIBUNWIND=3D1 make_no_backtrace :=3D NO_BACKTRACE=3D1 make_no_libcapstone :=3D NO_CAPSTONE=3D1 +make_libcapstone_dlopen :=3D LIBCAPSTONE_DLOPEN=3D1 make_no_libnuma :=3D NO_LIBNUMA=3D1 make_no_libbionic :=3D NO_LIBBIONIC=3D1 make_no_libbpf :=3D NO_LIBBPF=3D1 @@ -159,6 +160,7 @@ run +=3D make_libunwind run +=3D make_no_libdw_dwarf_unwind run +=3D make_no_backtrace run +=3D make_no_libcapstone +run +=3D make_libcapstone_dlopen run +=3D make_no_libnuma run +=3D make_no_libbionic run +=3D make_no_libbpf diff --git a/tools/perf/util/Build b/tools/perf/util/Build index b9925c6902ca..c037b1e99d28 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -11,7 +11,7 @@ perf-util-y +=3D block-info.o perf-util-y +=3D block-range.o perf-util-y +=3D build-id.o perf-util-y +=3D cacheline.o -perf-util-y +=3D capstone.o +perf-util-$(CONFIG_LIBCAPSTONE) +=3D capstone.o perf-util-y +=3D config.o perf-util-y +=3D copyfile.o perf-util-y +=3D ctype.o diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c index 9216916f848f..25cf6e15ec27 100644 --- a/tools/perf/util/capstone.c +++ b/tools/perf/util/capstone.c @@ -11,20 +11,137 @@ #include "print_insn.h" #include "symbol.h" #include "thread.h" +#include #include #include +#include #include =20 -#ifdef HAVE_LIBCAPSTONE_SUPPORT #include + +#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 =3D true; + dll_handle =3D 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 *handl= e); + + if (!fn_init) { + fn =3D dlsym(perf_cs_dll_handle(), "cs_open"); + if (!fn) + pr_debug("dlsym failed for cs_open\n"); + fn_init =3D 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 =3D dlsym(perf_cs_dll_handle(), "cs_option"); + if (!fn) + pr_debug("dlsym failed for cs_option\n"); + fn_init =3D 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_siz= e, + uint64_t address, size_t count, struct cs_insn **insn); + + if (!fn_init) { + fn =3D dlsym(perf_cs_dll_handle(), "cs_disasm"); + if (!fn) + pr_debug("dlsym failed for cs_disasm\n"); + fn_init =3D 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 =3D dlsym(perf_cs_dll_handle(), "cs_free"); + if (!fn) + pr_debug("dlsym failed for cs_free\n"); + fn_init =3D 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 =3D dlsym(perf_cs_dll_handle(), "cs_close"); + if (!fn) + pr_debug("dlsym failed for cs_close\n"); + fn_init =3D true; + } + if (!fn) + return CS_ERR_HANDLE; + return fn(handle); +#endif +} =20 -#ifdef HAVE_LIBCAPSTONE_SUPPORT static int capstone_init(struct machine *machine, csh *cs_handle, bool is6= 4, bool disassembler_style) { - cs_arch arch; - cs_mode mode; + enum cs_arch arch; + enum cs_mode mode; =20 if (machine__is(machine, "x86_64") && is64) { arch =3D CS_ARCH_X86; @@ -45,7 +162,7 @@ static int capstone_init(struct machine *machine, csh *c= s_handle, bool is64, return -1; } =20 - if (cs_open(arch, mode, cs_handle) !=3D CS_ERR_OK) { + if (perf_cs_open(arch, mode, cs_handle) !=3D 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); } =20 return 0; } -#endif =20 -#ifdef HAVE_LIBCAPSTONE_SUPPORT -static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *i= nsn, +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 =3D 0; =20 if (insn->detail && insn->detail->x86.op_count =3D=3D 1) { - cs_x86_op *op =3D &insn->detail->x86.operands[0]; + struct cs_x86_op *op =3D &insn->detail->x86.operands[0]; =20 addr_location__init(&al); if (op->type =3D=3D X86_OP_IMM && @@ -95,7 +210,6 @@ static size_t print_insn_x86(struct thread *thread, u8 c= pumode, cs_insn *insn, printed +=3D fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str); return printed; } -#endif =20 =20 ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused, @@ -106,9 +220,8 @@ ssize_t capstone__fprintf_insn_asm(struct machine *mach= ine __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 *mach= ine __maybe_unused, if (ret < 0) return ret; =20 - count =3D cs_disasm(cs_handle, code, code_size, ip, 1, &insn); + count =3D perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn); if (count > 0) { if (machine__normalized_is(machine, "x86")) printed =3D print_insn_x86(thread, cpumode, &insn[0], print_opts, fp); @@ -126,20 +239,16 @@ ssize_t capstone__fprintf_insn_asm(struct machine *ma= chine __maybe_unused, printed =3D fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str); if (lenp) *lenp =3D insn->size; - cs_free(insn, count); + perf_cs_free(insn, count); } else { printed =3D -1; } =20 - cs_close(&cs_handle); + perf_cs_close(&cs_handle); return printed; -#else - return -1; -#endif } =20 -#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; =20 for (i =3D 0; i < insn->detail->x86.op_count; i++) { - cs_x86_op *op =3D &insn->detail->x86.operands[i]; + struct cs_x86_op *op =3D &insn->detail->x86.operands[i]; u64 orig_addr; =20 if (op->type !=3D X86_OP_MEM) @@ -195,9 +304,7 @@ static void print_capstone_detail(cs_insn *insn, char *= buf, size_t len, break; } } -#endif =20 -#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 p= goff, void *arg) } return 0; } -#endif =20 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 =3D symbol__annotation(sym); struct map *map =3D args->ms->map; struct dso *dso =3D 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 =3D NULL; + struct cs_insn *insn =3D NULL; char disasm_buf[512]; struct disasm_line *dl; bool disassembler_style =3D false; @@ -274,7 +379,7 @@ int symbol__disassemble_capstone(const char *filename _= _maybe_unused, =20 needs_cs_close =3D true; =20 - free_count =3D count =3D cs_disasm(handle, buf, buf_len, start, buf_len, = &insn); + free_count =3D count =3D perf_cs_disasm(handle, buf, buf_len, start, buf_= len, &insn); for (i =3D 0, offset =3D 0; i < count; i++) { int printed; =20 @@ -313,9 +418,9 @@ int symbol__disassemble_capstone(const char *filename _= _maybe_unused, =20 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 =3D -1; goto out; -#else - return -1; -#endif } =20 int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unus= ed, struct symbol *sym __maybe_unused, struct annotate_args *args __maybe_unused) { -#ifdef HAVE_LIBCAPSTONE_SUPPORT struct annotation *notes =3D symbol__annotation(sym); struct map *map =3D args->ms->map; struct dso *dso =3D map__dso(map); @@ -458,7 +559,7 @@ int symbol__disassemble_capstone_powerpc(const char *fi= lename __maybe_unused, =20 out: if (needs_cs_close) - cs_close(&handle); + perf_cs_close(&handle); free(buf); return count < 0 ? count : 0; =20 @@ -467,7 +568,4 @@ int symbol__disassemble_capstone_powerpc(const char *fi= lename __maybe_unused, close(fd); count =3D -1; goto out; -#else - return -1; -#endif } diff --git a/tools/perf/util/capstone.h b/tools/perf/util/capstone.h index 0f030ea034b6..7c0baaa01a73 100644 --- a/tools/perf/util/capstone.h +++ b/tools/perf/util/capstone.h @@ -6,6 +6,7 @@ #include #include #include +#include #include =20 struct annotate_args; @@ -13,6 +14,7 @@ struct machine; struct symbol; struct thread; =20 +#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, s= truct symbol *sym, int symbol__disassemble_capstone_powerpc(const char *filename, struct symb= ol *sym, struct annotate_args *args); =20 +#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 __mayb= e_unused, + struct symbol *sym __maybe_unused, + struct annotate_args *args __maybe_unused) +{ + return -1; +} + +static inline int symbol__disassemble_capstone_powerpc(const char *filenam= e __maybe_unused, + struct symbol *sym __maybe_unused, + struct annotate_args *args __maybe_unused) +{ + return -1; +} + +#endif /* HAVE_LIBCAPSTONE_SUPPORT */ + #endif /* __PERF_CAPSTONE_H */ --=20 2.53.0.rc1.225.gd81095ad13-goog