From nobody Wed Dec 17 21:14:01 2025 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (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 75AB62BE053 for ; Sun, 5 Oct 2025 21:22:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759699362; cv=none; b=HiEehII9DB/AudTQvRH9ndIaclyd8T3j2V2JEOz58Ae/xBjCaoHnDqm+ZG+MIrgbP60LTojW60UfUlf/20/aq4+wHnrB2sk6wr3NZvfv9NiQ+EUkt/4CVgDDuY2owM3v+HDRicShtYb+q521VEtYgMHMbZNqjxCiogkCZXH/7cA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759699362; c=relaxed/simple; bh=aEAtMjVjgulTf0TDqlU6QiBFacJWqCSnPGZkk3a6iVs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Content-Type; b=Kwwyt1tFXgw29eXvby9ORqgCtOhph9fYec5YRyz9/L5hOES6BXPWOKIQGkq3iQtkoxv1dZbeXfrOrLEzrd57zcvdFmMOEb2z3fRBTnfYuitA+7fmQM8ERFsvwW0m/PNH1q8IUJn2j6agePGmqO8bL66DIV2BduKj6vaSCYuLK5w= 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=g1BEeBLF; arc=none smtp.client-ip=209.85.214.201 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="g1BEeBLF" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-28bd8b3fa67so34376775ad.2 for ; Sun, 05 Oct 2025 14:22:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1759699360; x=1760304160; darn=vger.kernel.org; h=to:from:subject:message-id:references:mime-version:in-reply-to:date :from:to:cc:subject:date:message-id:reply-to; bh=+k0ZT6dX6FpTW7iWOOUOC483WZu4iMuIQMOn3S3WjJM=; b=g1BEeBLFFwWVpuqwa+JCkgKBQASduUMGGVMyOVFJlfe8B9g/d7YhRYbY3FgAWW17Pa aMTJZt6TvshUeJdXCv5Qixdb9/7NUoy4z+pkS3MSF4LNlO+viJBRqxBTU/BZKdUDX2px JBiy8bThOzQkXntT6VA9xnzDx79tn6e4e/eS6CKClq4rGJnuhv5iTA6JqUJ7c2Cn/S2x BIdzWBUmjE421219xVHZHLfrq7+ziMNWJZDSTX3qZ4KtWmSt3z96+0EDvnb9a01F1p7H lJBb/Iw5yWGxe6EZBJHFsBQnXDgk2C8jeyH8CYLxK2l5mgdINuck48kmHQgRR5NL5zL/ AukA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759699360; x=1760304160; h=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=+k0ZT6dX6FpTW7iWOOUOC483WZu4iMuIQMOn3S3WjJM=; b=NGiidm56JFV3DCnhvw8edKZTVDYnirAVkHl9uHZBxnkxADGl4fWG8mSxj88lcb9J3Z UULVa8G5B/H1Ef1GnG8c/gdCqSOa2qaqpdQFvxQo6Axnp8JRq/6rZbIZB3aoDF5S6R7N 7bI72Oe6fYR+lXogrhnUotKnrwSiT8JINXnXc5DdltpiIAlrv1MwmRh509RtdWiByAKR zuvYKvZ/g9AocOxDiMJdehZr1NcBMWXpyOM5jqNNLUD+Y+MPxfdV0SPAg652JBGRhRH8 q8t4k1zS+wOqZZ4sCQB3sx+g5elLkVAJrxNJ0ysoYFG9M5+QJwzSmi3QzTjQy1+HVTXH LI+w== X-Forwarded-Encrypted: i=1; AJvYcCVQLA495dPzivQ1r+Xuzh+zQVmqTv7Cg8AUGKk7jKOoNs4GIWVMnuX9lL+mcPW+6W4ps0slhhMrcF/DGiQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyCIxaMSfhfwPx3thBuqurakXmiOOjiHkGuHHzLhz681ed9t0wo FHaYfzxWUjZamqwUkLquVzdbTrweYvcKTeQ4ccwNP43dyxBIkiEw+HeBOuZGGcCkMKp39Xs78v/ ZBmjIRmHKkw== X-Google-Smtp-Source: AGHT+IE7TzRaz3P02u5ZiI8lFQ+hJj2uct7i7p47hLFvgQQWhDJOz8AiZOgEexNWWsBsjd/RVFefG8yDhlc4 X-Received: from pjbjx18.prod.google.com ([2002:a17:90b:46d2:b0:32e:b87b:6c84]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:dacd:b0:267:9aa5:f6a6 with SMTP id d9443c01a7336-28e9a566722mr140467185ad.19.1759699359809; Sun, 05 Oct 2025 14:22:39 -0700 (PDT) Date: Sun, 5 Oct 2025 14:22:10 -0700 In-Reply-To: <20251005212212.2892175-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: <20251005212212.2892175-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.618.g983fd99d29-goog Message-ID: <20251005212212.2892175-10-irogers@google.com> Subject: [PATCH v7 09/11] perf capstone: Support for dlopen-ing libcapstone.so From: Ian Rogers To: Arnaldo Carvalho de Melo , Peter Zijlstra , Ingo Molnar , Namhyung Kim , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Charlie Jenkins , Eric Biggers , "Masami Hiramatsu (Google)" , James Clark , Collin Funk , "Dr. David Alan Gilbert" , Li Huafei , Athira Rajeev , Stephen Brennan , Dmitry Vyukov , Alexandre Ghiti , Haibo Xu , Andi Kleen , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, bpf@vger.kernel.org, llvm@lists.linux.dev, Song Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" If perf wasn't built against libcapstone, no HAVE_LIBCAPSTONE_SUPPORT, support dlopen-ing libcapstone.so and then calling the necessary functions by looking them up using dlsym. Reverse engineer the types in the API using pahole, adding only what's used in the perf code or necessary for the sake of struct size and alignment. Signed-off-by: Ian Rogers --- tools/perf/util/capstone.c | 285 ++++++++++++++++++++++++++++++++----- 1 file changed, 248 insertions(+), 37 deletions(-) diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c index be5fd44b1f9d..88e270237443 100644 --- a/tools/perf/util/capstone.c +++ b/tools/perf/util/capstone.c @@ -11,20 +11,250 @@ #include "print_insn.h" #include "symbol.h" #include "thread.h" +#include #include #include +#include #include =20 #ifdef HAVE_LIBCAPSTONE_SUPPORT #include +#else +typedef size_t csh; +enum cs_arch { + CS_ARCH_ARM =3D 0, + CS_ARCH_ARM64 =3D 1, + CS_ARCH_X86 =3D 3, + CS_ARCH_SYSZ =3D 6, +}; +enum cs_mode { + CS_MODE_ARM =3D 0, + CS_MODE_32 =3D 1 << 2, + CS_MODE_64 =3D 1 << 3, + CS_MODE_V8 =3D 1 << 6, + CS_MODE_BIG_ENDIAN =3D 1 << 31, +}; +enum cs_opt_type { + CS_OPT_SYNTAX =3D 1, + CS_OPT_DETAIL =3D 2, +}; +enum cs_opt_value { + CS_OPT_SYNTAX_ATT =3D 2, + CS_OPT_ON =3D 3, +}; +enum cs_err { + CS_ERR_OK =3D 0, + CS_ERR_HANDLE =3D 3, +}; +enum x86_op_type { + X86_OP_IMM =3D 2, + X86_OP_MEM =3D 3, +}; +enum x86_reg { + X86_REG_RIP =3D 41, +}; +typedef int32_t x86_avx_bcast; +struct x86_op_mem { + enum x86_reg segment; + enum x86_reg base; + enum x86_reg index; + int scale; + int64_t disp; +}; + +struct cs_x86_op { + enum x86_op_type type; + union { + enum x86_reg reg; + int64_t imm; + struct x86_op_mem mem; + }; + uint8_t size; + uint8_t access; + x86_avx_bcast avx_bcast; + bool avx_zero_opmask; +}; +struct cs_x86_encoding { + uint8_t modrm_offset; + uint8_t disp_offset; + uint8_t disp_size; + uint8_t imm_offset; + uint8_t imm_size; +}; +typedef int32_t x86_xop_cc; +typedef int32_t x86_sse_cc; +typedef int32_t x86_avx_cc; +typedef int32_t x86_avx_rm; +struct cs_x86 { + uint8_t prefix[4]; + uint8_t opcode[4]; + uint8_t rex; + uint8_t addr_size; + uint8_t modrm; + uint8_t sib; + int64_t disp; + enum x86_reg sib_index; + int8_t sib_scale; + enum x86_reg sib_base; + x86_xop_cc xop_cc; + x86_sse_cc sse_cc; + x86_avx_cc avx_cc; + bool avx_sae; + x86_avx_rm avx_rm; + union { + uint64_t eflags; + uint64_t fpu_flags; + }; + uint8_t op_count; + struct cs_x86_op operands[8]; + struct cs_x86_encoding encoding; +}; +struct cs_detail { + uint16_t regs_read[12]; + uint8_t regs_read_count; + uint16_t regs_write[20]; + uint8_t regs_write_count; + uint8_t groups[8]; + uint8_t groups_count; + + union { + struct cs_x86 x86; + }; +}; +struct cs_insn { + unsigned int id; + uint64_t address; + uint16_t size; + uint8_t bytes[16]; + char mnemonic[32]; + char op_str[160]; + struct cs_detail *detail; +}; +#endif + +#ifndef HAVE_LIBCAPSTONE_SUPPORT +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) +{ +#ifdef HAVE_LIBCAPSTONE_SUPPORT + 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) +{ +#ifdef HAVE_LIBCAPSTONE_SUPPORT + 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) +{ +#ifdef HAVE_LIBCAPSTONE_SUPPORT + 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 +} =20 +static void perf_cs_free(struct cs_insn *insn, size_t count) +{ #ifdef HAVE_LIBCAPSTONE_SUPPORT + 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) +{ +#ifdef HAVE_LIBCAPSTONE_SUPPORT + 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 +} + 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 +275,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 +287,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 +323,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 +333,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 +344,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 +352,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 +376,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 +417,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 +434,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 +453,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; @@ -273,7 +491,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 @@ -312,9 +530,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; @@ -334,16 +552,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); @@ -456,7 +670,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 @@ -465,7 +679,4 @@ int symbol__disassemble_capstone_powerpc(const char *fi= lename __maybe_unused, close(fd); count =3D -1; goto out; -#else - return -1; -#endif } --=20 2.51.0.618.g983fd99d29-goog