From nobody Sat Feb 7 08:02:42 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (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 25EE2328B7A for ; Tue, 27 Jan 2026 06:23:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769494991; cv=none; b=OEQmsCfVT01j6LANnptBLi81Q9S1vbqba5hVQR8tD/XEqyHEsaKaBN7iPT1NOIm/eqfCm0lSAHpuBq2kYBUWaLWXeMcpguNdSU/SDSp/SCZKO+ZxZDp08G00Pt3Y5b5H4QW7yD060HbEevsVRkbLWmfz5lkUB/J8D3wQbE28GYk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769494991; c=relaxed/simple; bh=X3lA5oHj9j5IOfLKOW/xaRcEDyTZRes6XgyV3JojUQw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=grBO/tGDcyUkR2OA222hDmIg+VI6z1MOvqYEmBCmXv3PllEF+mZ7KgVcOXKbCZYgbQeDlqDZ67Rg65MN1MYvenUYe02icmivQUXAAL2+H/1ZCnYp84WZEX4qMXf9QXhz62YoQCYsZS8vShHTq5jAIipVaq5S2aTXTT0W6PRR6Cc= 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=fh5drI3S; arc=none smtp.client-ip=74.125.82.73 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="fh5drI3S" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12337114f3fso7363845c88.0 for ; Mon, 26 Jan 2026 22:23:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769494989; x=1770099789; 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=4WdQOvLnp3Ng3z4VohvIiTNfVQDB3TmtY7IjRt4wfYs=; b=fh5drI3SEMlDvBRSStTM8jQYV8b5hV+eMhPzjnLcg3XOfmjdHFmZM6MllJVv03/9EZ JneQAt8sG2YShpmkUUFO4tQvfL87bSYUO3e5EPNKWzFIBwkufRaRdtbxbmcZH31ROeQy CTUUooVh7yQ+YcVS0kXph28VqVPwt55BdbsPXEpKeewT+4+mD/1fq2IKZohJs0QIE/0q QvYyD2pRNBJGOFUYCqPoPG50bmmhmUmiu0Z3lZi1bWP0CjOmmUc27xlZ9sLFZIm7sR/s HTjnS4f3d0Kp4cS6Oz8UoFGcBV0tRs8lRfuw4mwmtkhbGrrIrnXdaSEpHW+ERNplrpRf NKeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769494989; x=1770099789; 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=4WdQOvLnp3Ng3z4VohvIiTNfVQDB3TmtY7IjRt4wfYs=; b=Br2V7ANqZFfxsgzEf+ypv1Te0NsTl7oSUcrMwjlGH2xjkkMxmGhRQVT5qeEI1QJ6Rq 3jHwwuKWaVnqeaPYocEELuw9LmYfWWqlgW2YgqfpJyINO43ty4sbfQk6jKtX9Mm1O+fc UNbfy94DWbaG5Zqz7gin9joLEGWKjAhVUhqC8Vg/s0DYHNG7cNTCoomINtifOZuK4Fwq rFNkMarqJUKaB7v5xEdqyuJoRDoIrfe9VAmdyI8SkEzk/qZ5/xBA9Az1E8frxGJemctK 65poyZVlxbCgrLGVGr49Rfm1nqkM+YPJPecAmk/CrD6NeniSFgKtUS+oMJxq/UqImnRj qhDg== X-Forwarded-Encrypted: i=1; AJvYcCUEnCi6tXHYWxRomd6gQ2ERjRuLjMchxdGJPgt7pcS560gO5BJPdcibDIA6soh5USDj3K2rPZpQn0aCzrw=@vger.kernel.org X-Gm-Message-State: AOJu0Yx7pR2N4lXFLfCNE9qQz/+l3DJelZ6A6zZd+yXd9envfhx1vczF wjPdUBI+PeY6ENQ9Ami+zjLknFq87ZXk2S9myK6HXKvGyYr7bKGndhaiI+wSPS09+Rpf7/lf7NS ebYfbPRu7Ag== X-Received: from dlzz35.prod.google.com ([2002:a05:7022:4a3:b0:119:9f33:34a6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:2214:b0:121:72bb:3cd7 with SMTP id a92af1059eb24-124a006388cmr423524c88.7.1769494989211; Mon, 26 Jan 2026 22:23:09 -0800 (PST) Date: Mon, 26 Jan 2026 22:23:00 -0800 In-Reply-To: <20260127062302.544809-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: <20260127062302.544809-1-irogers@google.com> X-Mailer: git-send-email 2.52.0.457.g6b5491de43-goog Message-ID: <20260127062302.544809-2-irogers@google.com> Subject: [PATCH v9 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 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 9216916f848f..43168d43e4a3 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; @@ -274,7 +492,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 +531,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 +553,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 +672,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 +681,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.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 08:02:42 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (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 8AE0A329E58 for ; Tue, 27 Jan 2026 06:23:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769494994; cv=none; b=XF8WA+PxVgJA8dONMLsS61li0jZ46rM1ZIdS9hqIrGtCNP6XmQb9uWQZ1rwSD14nLPvxTAZI5iC57hHG0ZqgamoEhRQwVkzVlBaToOhrQ1VBAtkymf1CSpRumlyQYPbX7hXVVcuIOtVGFzcCXiCxmFrz6Xw57bDqxzxAsA7cMBc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769494994; c=relaxed/simple; bh=X9voLdpPW/fv1ohrRYVxmspHdmdgQklSNKpLkDHioI8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=H3h6jt0X9/m73dBovHXvYgcigjus3wQmes8/U+LvEyJX/YR2SygMoLN/RscaXtjPJvrAdsEqXnxY9Cdb6tSKlTf1lY4/OIR7OckW7hmsJwjMdkO8Lerkue9Y7MsRnrm/NPI4ax9Z93ioxB5SltNQWuTwzqMmyWCitezF+qwO8zI= 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=4kWO1ER/; arc=none smtp.client-ip=74.125.82.74 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="4kWO1ER/" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12337114f3fso7363903c88.0 for ; Mon, 26 Jan 2026 22:23:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769494992; x=1770099792; 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=X4rWhny6hZ4DeN5JQBc6YD7UhFb7fhBfkiggulUjHdw=; b=4kWO1ER/KlSDEVBq+aA6GuQGV1jhJ1ZEkhYSkoToo6VrH1rvsDS+1jGfAtGVjuik5s B0nEqOmWh6h36BPuu240afFAkBerVBzovIIBwOcTYX3d3tD3FxLQa1QA3Z3OHKyXP/P0 Bv0iabWwpw2Nm8Xu3nUAFn83tl/yrNDWGzDtJ4lf/UP78Y9LwFlw5haVt/aUlZ8Vc37p 0EYs12PXyEOGDDGr23rRj/yxI4H/xJUMjrfNMThL86xvI7ldQKtLkkt9ZxH6+ZyZpnQF V4sZK4fIx+Fjjz4LOmT8qrscWxwDt4PBj5Yaih027ZuGDU/ClPUBnCwsV9CQjvaHzqwm fddA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769494992; x=1770099792; 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=X4rWhny6hZ4DeN5JQBc6YD7UhFb7fhBfkiggulUjHdw=; b=xDv23GvxMuMvuO8w2lBumyi19oveXJptiz4CD+EV6OhrJ9gbUk97qXaVBsbeDy1nem /GLqFDiTYxBr58kw6oVc5g6MQ0eqSA3mjD4bmxjlqDN43vcWuEzj/SfsOT4Dd7lewQlB 54Njvy1e8f+gNas5CW3fNYrhmFVyAG2OotzFTGciLJxwVoJnVVOPezIX/gnnBvbc7KGU 6EL5qHjlZE8HzYkRYaWctSg0kXsv1i8kt7R/2DY8LPWhk6ybdI1ANJljd13uZebrl7HA qybx9YT1fzJYbtr17k8cOHLaXR9Fe1eWeZ5AYba0gpkmOs9iPh0VIR39hGm4OD22D9f5 VMqA== X-Forwarded-Encrypted: i=1; AJvYcCWEFS69t07tAySTxzNr27QUFtfE5l8gCEDB+O8F0aNC2Oh5sRmif1ZGgr+VtQxPemX/5b8omiRTxbFxjSA=@vger.kernel.org X-Gm-Message-State: AOJu0YzzQq4x7cBFafoCvQ+DkKpyWhP9EfI3UdHecUnzscFl/E1C8bKi BNvk49OYtn76RWhRo3bBTYtuekiBfNnEl2LqA+UAVqbhbK0YI2sG32bPjblFCltXV1y09A5HEvg Rnq212ol6IQ== X-Received: from dlbtu19.prod.google.com ([2002:a05:7022:3c13:b0:123:2dd9:db4]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6286:b0:11b:9386:a37d with SMTP id a92af1059eb24-124a00e8ae7mr329280c88.44.1769494991507; Mon, 26 Jan 2026 22:23:11 -0800 (PST) Date: Mon, 26 Jan 2026 22:23:01 -0800 In-Reply-To: <20260127062302.544809-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: <20260127062302.544809-1-irogers@google.com> X-Mailer: git-send-email 2.52.0.457.g6b5491de43-goog Message-ID: <20260127062302.544809-3-irogers@google.com> Subject: [PATCH v9 2/3] perf llvm: Support for dlopen-ing libLLVM.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 wasn't built against libLLVM, no HAVE_LIBLLVM_SUPPORT, support dlopen-ing libLLVM.so and then calling the necessary functions by looking them up using dlsym. As the C++ code in llvm-c-helpers used for addr2line is problematic to call using dlsym by default don't support its use if HAVE_LIBLLVM_SUPPORT isn't present. Add a new build option LIBLLVM_DYNAMIC=3D1 that builds a separate shared object called libperf-llvm.so containing the LLVM addr2line functionality and support dynamic loading of it. Signed-off-by: Ian Rogers --- tools/perf/Makefile.config | 13 ++ tools/perf/Makefile.perf | 23 ++- tools/perf/tests/make | 2 + tools/perf/util/Build | 2 +- tools/perf/util/llvm-c-helpers.cpp | 113 +++++++++++- tools/perf/util/llvm.c | 273 +++++++++++++++++++++++++---- 6 files changed, 388 insertions(+), 38 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 63ca9b2be663..5b99fd221d8f 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -964,6 +964,19 @@ ifndef NO_LIBLLVM NO_LIBLLVM :=3D 1 endif endif +ifdef LIBLLVM_DYNAMIC + ifndef NO_LIBLLVM + $(error LIBLLVM_DYNAMIC should be used with NO_LIBLLVM) + endif + $(call feature_check,llvm-perf) + ifneq ($(feature-llvm-perf), 1) + $(warning LIBLLVM_DYNAMIC requires libLLVM.so which wasn't feature det= ected) + endif + CFLAGS +=3D -DHAVE_LIBLLVM_DYNAMIC + CFLAGS +=3D $(shell $(LLVM_CONFIG) --cflags) + CXXFLAGS +=3D -DHAVE_LIBLLVM_DYNAMIC + CXXFLAGS +=3D $(shell $(LLVM_CONFIG) --cxxflags) +endif =20 ifndef NO_DEMANGLE $(call feature_check,cxa-demangle) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 45d5a59a02cb..30e16a564d60 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -422,6 +422,12 @@ ifndef NO_JVMTI PROGRAMS +=3D $(OUTPUT)$(LIBJVMTI) endif =20 +LIBPERF_LLVM =3D libperf-llvm.so + +ifdef LIBLLVM_DYNAMIC +PROGRAMS +=3D $(OUTPUT)$(LIBPERF_LLVM) +endif + DLFILTERS :=3D dlfilter-test-api-v0.so dlfilter-test-api-v2.so dlfilter-sh= ow-cycles.so DLFILTERS :=3D $(patsubst %,$(OUTPUT)dlfilters/%,$(DLFILTERS)) =20 @@ -989,6 +995,16 @@ $(LIBSYMBOL)-clean: $(call QUIET_CLEAN, libsymbol) $(Q)$(RM) -r -- $(LIBSYMBOL_OUTPUT) =20 +ifdef LIBLLVM_DYNAMIC +LIBPERF_LLVM_CXXFLAGS :=3D $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXF= LAGS)) -DHAVE_LIBLLVM_SUPPORT +LIBPERF_LLVM_LIBS =3D -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) -lstdc= ++ + +$(OUTPUT)$(LIBPERF_LLVM): util/llvm-c-helpers.cpp + $(QUIET_LINK)$(CXX) $(LIBPERF_LLVM_CXXFLAGS) $(LIBPERF_LLVM_LIBS) -shared= -o $@ $< + +$(OUTPUT)perf: $(OUTPUT)$(LIBPERF_LLVM) +endif + help: @echo 'Perf make targets:' @echo ' doc - make *all* documentation (see below)' @@ -1090,6 +1106,11 @@ ifndef NO_JVMTI $(call QUIET_INSTALL, $(LIBJVMTI)) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \ $(INSTALL) $(OUTPUT)$(LIBJVMTI) '$(DESTDIR_SQ)$(libdir_SQ)'; +endif +ifdef LIBLLVM_DYNAMIC + $(call QUIET_INSTALL, $(LIBPERF_LLVM)) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \ + $(INSTALL) $(OUTPUT)$(LIBPERF_LLVM) '$(DESTDIR_SQ)$(libdir_SQ)'; endif $(call QUIET_INSTALL, libexec) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' @@ -1290,7 +1311,7 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-= clean $(LIBSYMBOL)-clean $( -name '\.*.cmd' -delete -o -name '\.*.d' -delete -o -name '*.shellcheck_= log' -delete $(Q)$(RM) $(OUTPUT).config-detected $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso= 32 \ - perf-read-vdsox32 $(OUTPUT)$(LIBJVMTI).so + perf-read-vdsox32 $(OUTPUT)$(LIBJVMTI) $(OUTPUT)$(LIBPERF_LLVM) $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo= \ TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE \ $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 767ad9e147a8..43faf56d7504 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -91,6 +91,7 @@ make_no_libbpf :=3D NO_LIBBPF=3D1 make_libbpf_dynamic :=3D LIBBPF_DYNAMIC=3D1 make_no_libbpf_DEBUG :=3D NO_LIBBPF=3D1 DEBUG=3D1 make_no_libllvm :=3D NO_LIBLLVM=3D1 +make_libllvm_dynamic :=3D NO_LIBLLVM=3D1 LIBLLVM_DYNAMIC=3D1 make_with_babeltrace:=3D LIBBABELTRACE=3D1 make_with_coresight :=3D CORESIGHT=3D1 make_no_sdt :=3D NO_SDT=3D1 @@ -164,6 +165,7 @@ run +=3D make_no_libbionic run +=3D make_no_libbpf run +=3D make_no_libbpf_DEBUG run +=3D make_no_libllvm +run +=3D make_libllvm_dynamic run +=3D make_no_sdt run +=3D make_no_syscall_tbl run +=3D make_with_babeltrace diff --git a/tools/perf/util/Build b/tools/perf/util/Build index b9925c6902ca..8055336182fb 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -29,6 +29,7 @@ perf-util-y +=3D find_bit.o perf-util-y +=3D levenshtein.o perf-util-$(CONFIG_LIBBFD) +=3D libbfd.o perf-util-y +=3D llvm.o +perf-util-y +=3D llvm-c-helpers.o perf-util-y +=3D mmap.o perf-util-y +=3D memswap.o perf-util-y +=3D parse-events.o @@ -249,7 +250,6 @@ perf-util-$(CONFIG_CXX_DEMANGLE) +=3D demangle-cxx.o perf-util-y +=3D demangle-ocaml.o perf-util-y +=3D demangle-java.o perf-util-y +=3D demangle-rust-v0.o -perf-util-$(CONFIG_LIBLLVM) +=3D llvm-c-helpers.o =20 CFLAGS_demangle-rust-v0.o +=3D -Wno-shadow -Wno-declaration-after-statemen= t \ -Wno-switch-default -Wno-switch-enum -Wno-missing-field-initializers diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-he= lpers.cpp index 004081bd12c9..5a6f76e6b705 100644 --- a/tools/perf/util/llvm-c-helpers.cpp +++ b/tools/perf/util/llvm-c-helpers.cpp @@ -5,17 +5,23 @@ * macros (e.g. noinline) that conflict with compiler builtins used * by LLVM. */ +#ifdef HAVE_LIBLLVM_SUPPORT #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" /* Needed for LLVM <= =3D 15 */ #include #include #pragma GCC diagnostic pop +#endif =20 +#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC) +#include +#endif #include #include #include #include extern "C" { +#include "debug.h" #include } #include "llvm-c-helpers.h" @@ -23,14 +29,33 @@ extern "C" { extern "C" char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name= ); =20 +#ifdef HAVE_LIBLLVM_SUPPORT using namespace llvm; using llvm::symbolize::LLVMSymbolizer; +#endif + +#if !defined(HAVE_LIBLLVM_SUPPORT) && defined(HAVE_LIBLLVM_DYNAMIC) +static void *perf_llvm_c_helpers_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("libperf-llvm.so", RTLD_LAZY); + if (!dll_handle) + pr_debug("dlopen failed for libperf-llvm.so\n"); + } + return dll_handle; +} +#endif =20 /* * Allocate a static LLVMSymbolizer, which will live to the end of the pro= gram. * Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not ne= ed * to store anything in the dso struct. */ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) static LLVMSymbolizer *get_symbolizer() { static LLVMSymbolizer *instance =3D nullptr; @@ -49,8 +74,10 @@ static LLVMSymbolizer *get_symbolizer() } return instance; } +#endif =20 /* Returns 0 on error, 1 on success. */ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) static int extract_file_and_line(const DILineInfo &line_info, char **file, unsigned int *line) { @@ -69,13 +96,15 @@ static int extract_file_and_line(const DILineInfo &line= _info, char **file, *line =3D line_info.Line; return 1; } +#endif =20 extern "C" -int llvm_addr2line(const char *dso_name, u64 addr, - char **file, unsigned int *line, - bool unwind_inlines, - llvm_a2l_frame **inline_frames) +int llvm_addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_u= nused, + char **file __maybe_unused, unsigned int *line __maybe_unused, + bool unwind_inlines __maybe_unused, + llvm_a2l_frame **inline_frames __maybe_unused) { +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) LLVMSymbolizer *symbolizer =3D get_symbolizer(); object::SectionedAddress sectioned_addr =3D { addr, @@ -135,8 +164,33 @@ int llvm_addr2line(const char *dso_name, u64 addr, return 0; return extract_file_and_line(*res_or_err, file, line); } +#elif defined(HAVE_LIBLLVM_DYNAMIC) + static bool fn_init; + static int (*fn)(const char *dso_name, u64 addr, + char **file, unsigned int *line, + bool unwind_inlines, + llvm_a2l_frame **inline_frames); + + if (!fn_init) { + void * handle =3D perf_llvm_c_helpers_dll_handle(); + + if (!handle) + return 0; + + fn =3D reinterpret_cast(dlsym(handle, "llvm_addr2line")); + if (!fn) + pr_debug("dlsym failed for llvm_addr2line\n"); + fn_init =3D true; + } + if (!fn) + return 0; + return fn(dso_name, addr, file, line, unwind_inlines, inline_frames); +#else + return 0; +#endif } =20 +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) static char * make_symbol_relative_string(struct dso *dso, const char *sym_name, u64 addr, u64 base_addr) @@ -158,10 +212,13 @@ make_symbol_relative_string(struct dso *dso, const ch= ar *sym_name, return strdup(sym_name); } } +#endif =20 extern "C" -char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr) +char *llvm_name_for_code(struct dso *dso __maybe_unused, const char *dso_n= ame __maybe_unused, + u64 addr __maybe_unused) { +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) LLVMSymbolizer *symbolizer =3D get_symbolizer(); object::SectionedAddress sectioned_addr =3D { addr, @@ -175,11 +232,34 @@ char *llvm_name_for_code(struct dso *dso, const char = *dso_name, u64 addr) return make_symbol_relative_string( dso, res_or_err->FunctionName.c_str(), addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0); +#elif defined(HAVE_LIBLLVM_DYNAMIC) + static bool fn_init; + static char *(*fn)(struct dso *dso, const char *dso_name, u64 addr); + + if (!fn_init) { + void * handle =3D perf_llvm_c_helpers_dll_handle(); + + if (!handle) + return NULL; + + fn =3D reinterpret_cast(dlsym(handle, "llvm_name_for_code"= )); + if (!fn) + pr_debug("dlsym failed for llvm_name_for_code\n"); + fn_init =3D true; + } + if (!fn) + return NULL; + return fn(dso, dso_name, addr); +#else + return 0; +#endif } =20 extern "C" -char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr) +char *llvm_name_for_data(struct dso *dso __maybe_unused, const char *dso_n= ame __maybe_unused, + u64 addr __maybe_unused) { +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) LLVMSymbolizer *symbolizer =3D get_symbolizer(); object::SectionedAddress sectioned_addr =3D { addr, @@ -193,4 +273,25 @@ char *llvm_name_for_data(struct dso *dso, const char *= dso_name, u64 addr) return make_symbol_relative_string( dso, res_or_err->Name.c_str(), addr, res_or_err->Start); +#elif defined(HAVE_LIBLLVM_DYNAMIC) + static bool fn_init; + static char *(*fn)(struct dso *dso, const char *dso_name, u64 addr); + + if (!fn_init) { + void * handle =3D perf_llvm_c_helpers_dll_handle(); + + if (!handle) + return NULL; + + fn =3D reinterpret_cast(dlsym(handle, "llvm_name_for_data"= )); + if (!fn) + pr_debug("dlsym failed for llvm_name_for_data\n"); + fn_init =3D true; + } + if (!fn) + return NULL; + return fn(dso, dso_name, addr); +#else + return 0; +#endif } diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c index 0d126d233c01..b4c6c15fd2dc 100644 --- a/tools/perf/util/llvm.c +++ b/tools/perf/util/llvm.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "llvm.h" +#include "llvm-c-helpers.h" #include "annotate.h" #include "debug.h" #include "dso.h" @@ -7,18 +8,244 @@ #include "namespaces.h" #include "srcline.h" #include "symbol.h" +#include #include #include +#include #include #include =20 -#ifdef HAVE_LIBLLVM_SUPPORT -#include "llvm-c-helpers.h" +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) #include #include +#else +typedef void *LLVMDisasmContextRef; +typedef int (*LLVMOpInfoCallback)(void *dis_info, uint64_t pc, uint64_t of= fset, + uint64_t op_size, uint64_t inst_size, + int tag_type, void *tag_buf); +typedef const char *(*LLVMSymbolLookupCallback)(void *dis_info, + uint64_t reference_value, + uint64_t *reference_type, + uint64_t reference_pc, + const char **reference_name); +#define LLVMDisassembler_ReferenceType_InOut_None 0 +#define LLVMDisassembler_ReferenceType_In_Branch 1 +#define LLVMDisassembler_ReferenceType_In_PCrel_Load 2 +#define LLVMDisassembler_Option_PrintImmHex 2 +#define LLVMDisassembler_Option_AsmPrinterVariant 4 +const char *llvm_targets[] =3D { + "AMDGPU", + "ARM", + "AVR", + "BPF", + "Hexagon", + "Lanai", + "LoongArch", + "Mips", + "MSP430", + "NVPTX", + "PowerPC", + "RISCV", + "Sparc", + "SystemZ", + "VE", + "WebAssembly", + "X86", + "XCore", + "M68k", + "Xtensa", +}; +#endif + +#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC) +static void *perf_llvm_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("libLLVM.so", RTLD_LAZY); + if (!dll_handle) + pr_debug("dlopen failed for libLLVM.so\n"); + } + return dll_handle; +} +#endif + +#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC) +static void *perf_llvm_dll_fun(const char *fmt, const char *target) +{ + char buf[128]; + void *fn; + + snprintf(buf, sizeof(buf), fmt, target); + fn =3D dlsym(perf_llvm_dll_handle(), buf); + if (!fn) + pr_debug("dlsym failed for %s\n", buf); + + return fn; +} +#endif + +static void perf_LLVMInitializeAllTargetInfos(void) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + LLVMInitializeAllTargetInfos(); +#else + /* LLVMInitializeAllTargetInfos is a header file function not available a= s a symbol. */ + static bool done_init; + + if (done_init) + return; + + for (size_t i =3D 0; i < ARRAY_SIZE(llvm_targets); i++) { + void (*fn)(void) =3D perf_llvm_dll_fun("LLVMInitialize%sTargetInfo", + llvm_targets[i]); + + if (!fn) + continue; + fn(); + } + done_init =3D true; +#endif +} + +static void perf_LLVMInitializeAllTargetMCs(void) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + LLVMInitializeAllTargetMCs(); +#else + /* LLVMInitializeAllTargetMCs is a header file function not available as = a symbol. */ + static bool done_init; + + if (done_init) + return; + + for (size_t i =3D 0; i < ARRAY_SIZE(llvm_targets); i++) { + void (*fn)(void) =3D perf_llvm_dll_fun("LLVMInitialize%sTargetMC", + llvm_targets[i]); + + if (!fn) + continue; + fn(); + } + done_init =3D true; +#endif +} + +static void perf_LLVMInitializeAllDisassemblers(void) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + LLVMInitializeAllDisassemblers(); +#else + /* LLVMInitializeAllDisassemblers is a header file function not available= as a symbol. */ + static bool done_init; + + if (done_init) + return; + + for (size_t i =3D 0; i < ARRAY_SIZE(llvm_targets); i++) { + void (*fn)(void) =3D perf_llvm_dll_fun("LLVMInitialize%sDisassembler", + llvm_targets[i]); + + if (!fn) + continue; + fn(); + } + done_init =3D true; +#endif +} + +static LLVMDisasmContextRef perf_LLVMCreateDisasm(const char *triple_name,= void *dis_info, + int tag_type, LLVMOpInfoCallback get_op_info, + LLVMSymbolLookupCallback symbol_lookup) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + return LLVMCreateDisasm(triple_name, dis_info, tag_type, get_op_info, sym= bol_lookup); +#else + static bool fn_init; + static LLVMDisasmContextRef (*fn)(const char *triple_name, void *dis_info, + int tag_type, LLVMOpInfoCallback get_op_info, + LLVMSymbolLookupCallback symbol_lookup); + + if (!fn_init) { + fn =3D dlsym(perf_llvm_dll_handle(), "LLVMCreateDisasm"); + if (!fn) + pr_debug("dlsym failed for LLVMCreateDisasm\n"); + fn_init =3D true; + } + if (!fn) + return NULL; + return fn(triple_name, dis_info, tag_type, get_op_info, symbol_lookup); +#endif +} + +static int perf_LLVMSetDisasmOptions(LLVMDisasmContextRef context, uint64_= t options) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + return LLVMSetDisasmOptions(context, options); +#else + static bool fn_init; + static int (*fn)(LLVMDisasmContextRef context, uint64_t options); + + if (!fn_init) { + fn =3D dlsym(perf_llvm_dll_handle(), "LLVMSetDisasmOptions"); + if (!fn) + pr_debug("dlsym failed for LLVMSetDisasmOptions\n"); + fn_init =3D true; + } + if (!fn) + return 0; + return fn(context, options); +#endif +} + +static size_t perf_LLVMDisasmInstruction(LLVMDisasmContextRef context, uin= t8_t *bytes, + uint64_t bytes_size, uint64_t pc, + char *out_string, size_t out_string_size) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + return LLVMDisasmInstruction(context, bytes, bytes_size, pc, out_string, = out_string_size); +#else + static bool fn_init; + static int (*fn)(LLVMDisasmContextRef context, uint8_t *bytes, + uint64_t bytes_size, uint64_t pc, + char *out_string, size_t out_string_size); + + if (!fn_init) { + fn =3D dlsym(perf_llvm_dll_handle(), "LLVMDisasmInstruction"); + if (!fn) + pr_debug("dlsym failed for LLVMDisasmInstruction\n"); + fn_init =3D true; + } + if (!fn) + return 0; + return fn(context, bytes, bytes_size, pc, out_string, out_string_size); +#endif +} + +static void perf_LLVMDisasmDispose(LLVMDisasmContextRef context) +{ +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) + LLVMDisasmDispose(context); +#else + static bool fn_init; + static int (*fn)(LLVMDisasmContextRef context); + + if (!fn_init) { + fn =3D dlsym(perf_llvm_dll_handle(), "LLVMDisasmDispose"); + if (!fn) + pr_debug("dlsym failed for LLVMDisasmDispose\n"); + fn_init =3D true; + } + if (!fn) + return; + fn(context); #endif +} + =20 -#ifdef HAVE_LIBLLVM_SUPPORT static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames, int num_frames) { @@ -30,14 +257,12 @@ static void free_llvm_inline_frames(struct llvm_a2l_fr= ame *inline_frames, zfree(&inline_frames); } } -#endif =20 int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_= unused, char **file __maybe_unused, unsigned int *line __maybe_unused, struct dso *dso __maybe_unused, bool unwind_inlines __maybe_unused, struct inline_node *node __maybe_unused, struct symbol *sym __maybe= _unused) { -#ifdef HAVE_LIBLLVM_SUPPORT struct llvm_a2l_frame *inline_frames =3D NULL; int num_frames =3D llvm_addr2line(dso_name, addr, file, line, node && unwind_inlines, &inline_frames); @@ -65,20 +290,16 @@ int llvm__addr2line(const char *dso_name __maybe_unuse= d, u64 addr __maybe_unused free_llvm_inline_frames(inline_frames, num_frames); =20 return num_frames; -#else - return -1; -#endif } =20 -#ifdef HAVE_LIBLLVM_SUPPORT static void init_llvm(void) { static bool init; =20 if (!init) { - LLVMInitializeAllTargetInfos(); - LLVMInitializeAllTargetMCs(); - LLVMInitializeAllDisassemblers(); + perf_LLVMInitializeAllTargetInfos(); + perf_LLVMInitializeAllTargetMCs(); + perf_LLVMInitializeAllDisassemblers(); init =3D true; } } @@ -111,12 +332,10 @@ symbol_lookup_callback(void *disinfo, uint64_t value, *ref_type =3D LLVMDisassembler_ReferenceType_InOut_None; return NULL; } -#endif =20 int symbol__disassemble_llvm(const char *filename, struct symbol *sym, struct annotate_args *args __maybe_unused) { -#ifdef HAVE_LIBLLVM_SUPPORT struct annotation *notes =3D symbol__annotation(sym); struct map *map =3D args->ms->map; struct dso *dso =3D map__dso(map); @@ -149,15 +368,15 @@ int symbol__disassemble_llvm(const char *filename, st= ruct symbol *sym, if (arch__is_x86(args->arch)) { const char *triplet =3D is_64bit ? "x86_64-pc-linux" : "i686-pc-linux"; =20 - disasm =3D LLVMCreateDisasm(triplet, &storage, /*tag_type=3D*/0, - /*get_op_info=3D*/NULL, symbol_lookup_callback); + disasm =3D perf_LLVMCreateDisasm(triplet, &storage, /*tag_type=3D*/0, + /*get_op_info=3D*/NULL, symbol_lookup_callback); } else { char triplet[64]; =20 scnprintf(triplet, sizeof(triplet), "%s-linux-gnu", args->arch->name); - disasm =3D LLVMCreateDisasm(triplet, &storage, /*tag_type=3D*/0, - /*get_op_info=3D*/NULL, symbol_lookup_callback); + disasm =3D perf_LLVMCreateDisasm(triplet, &storage, /*tag_type=3D*/0, + /*get_op_info=3D*/NULL, symbol_lookup_callback); } =20 if (disasm =3D=3D NULL) @@ -165,8 +384,7 @@ int symbol__disassemble_llvm(const char *filename, stru= ct symbol *sym, =20 if (args->options->disassembler_style && !strcmp(args->options->disassembler_style, "intel")) - LLVMSetDisasmOptions(disasm, - LLVMDisassembler_Option_AsmPrinterVariant); + perf_LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_AsmPrinterVari= ant); =20 /* * This needs to be set after AsmPrinterVariant, due to a bug in LLVM; @@ -174,7 +392,7 @@ int symbol__disassemble_llvm(const char *filename, stru= ct symbol *sym, * forget about the PrintImmHex flag (which is applied before if both * are given to the same call). */ - LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex); + perf_LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex); =20 /* add the function address and name */ scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", @@ -203,9 +421,9 @@ int symbol__disassemble_llvm(const char *filename, stru= ct symbol *sym, * LLVM's API has the code be disassembled as non-const, cast * here as we may be disassembling from mapped read-only memory. */ - ins_len =3D LLVMDisasmInstruction(disasm, (u8 *)(buf + offset), - buf_len - offset, pc, - disasm_buf, sizeof(disasm_buf)); + ins_len =3D perf_LLVMDisasmInstruction(disasm, (u8 *)(buf + offset), + buf_len - offset, pc, + disasm_buf, sizeof(disasm_buf)); if (ins_len =3D=3D 0) goto err; disasm_len =3D strlen(disasm_buf); @@ -261,13 +479,8 @@ int symbol__disassemble_llvm(const char *filename, str= uct symbol *sym, ret =3D 0; =20 err: - LLVMDisasmDispose(disasm); + perf_LLVMDisasmDispose(disasm); free(code_buf); free(line_storage); return ret; -#else // HAVE_LIBLLVM_SUPPORT - pr_debug("The LLVM disassembler isn't linked in for %s in %s\n", - sym->name, filename); - return -1; -#endif } --=20 2.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 08:02:42 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 C5FE032A3FE for ; Tue, 27 Jan 2026 06:23:14 +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=1769494996; cv=none; b=nGDqju6wcbVm9A0gWxpHGblDNvmFeUuiKLqD+mlijttq5vbUwg6/hj+WiHaCLLkGtWX4ykhDT5+ocoxlY8gzwmMh2qDamLi5XCPxDGuawR2sgj7ykDYqYgin5ukgLWD2wJHRx5RUq2FssX4vPoulhjQfUfd9AguAuNQRF8GAkHk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769494996; c=relaxed/simple; bh=DzpBYHNhf+IQZjOsk1aFwqpPOgzRL03WbqTvxyXXEQQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=GVT3C0jye4ky50/oTYYa3HznpgtCaBDZG0AvT+BJmxdjjBGjKPLu3UTECysv69ngog+6+2Kc10IWB7tvruVL4+krrbWcuCyFWxd3NK9GCGm5F0A2Ttn63Ur9ec30s42kef2szM6LNW/bEXf5EDHL57yI7QbIm5cQAlo06mti//Q= 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=dB89mgwk; 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="dB89mgwk" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2b71c5826fbso5652074eec.1 for ; Mon, 26 Jan 2026 22:23:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769494994; x=1770099794; 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=+RQGwZdDM6aunvixP8g7wOTGkfjHo/SchVM6nUgdLuw=; b=dB89mgwknHTegZ/OWg5i4sbu2iC4s2v8ry4hi9/HUGf4pdkY6NPsJwRhB3YiIw3u8U 2+/ARoGLNSnezUEECO/3Ni5j3Nz2iGXrGcUEHWgBlw6r0l/Yh/5CD0OLg7DdecEYiIy5 f+6wJHUllF2X4lF0nWKnWr4/kTgy+FWxMVeZrwcFWh/eKT8gM9BC6J9vLS9tW7PGwiHb GbUYWPSNMffIfATUpHQVrOCtWSberwYGailyqLN39ocksOL4CNuYZQRKBwKf2iFUCqLQ nqOauCGOVGUNmS+w5aibZ1WYV0KoW3zijMyM0jKJLgFHjbQpOs/rJNtbcw9zfzycv6Q3 vWJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769494994; x=1770099794; 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=+RQGwZdDM6aunvixP8g7wOTGkfjHo/SchVM6nUgdLuw=; b=OanFxLKo6fChvtOlKkku0kDZYLZ6ZYSTMcO4KIexjJr6W2RPwZ4vnmJKJmUPVoxb+K YbKhU2/sU73G6Hw6PC/zd9+v3YoDquR9iC1Wq+geBiD4pDD9Cof2x+3RFrjCzwT/tNKz zWAl1IEzsxE99fpKAkKSMM54ZSnMWZKEUEoBuI//6sA70ps//gyGdz8n9vrfg6pVfqrx R7aIy7QruXaQW+6thuqTSB8tLErz9Du1XEGqpPC6RJqC51GTW1cR2DYUGddGZNo0HJKJ YZDQCrA+s9iT7pCekvT2CcDhymnUUscNlNW7sp0kz4j1xvZ1V8dS3rm6IFGmnBUcnsx6 5iFg== X-Forwarded-Encrypted: i=1; AJvYcCVJ3c1uWmdgt3+ZwLEOyZXVITXMUoI754Lk9URxw5FDCnfU+90hJOLtyBwc5JuYEvRqEwdRzVyd+xt7xMI=@vger.kernel.org X-Gm-Message-State: AOJu0Yy6Xhp/HVdMk/fMOampjNNZB4tGKwBupk2OBOmo6euYqH+UUWdX DgY8WqNvKfjtDYQj6EewVOxn6XHH6hHUH3xEZ17SuiVAqTszU55Jlg/8L7uKf1+EswdDRcAD6CJ ERAsLao1+Bw== X-Received: from dlbcf5.prod.google.com ([2002:a05:7022:4585:b0:123:291f:3e5b]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:6ca1:b0:2b0:5342:e00a with SMTP id 5a478bee46e88-2b78d90c31fmr455260eec.15.1769494993905; Mon, 26 Jan 2026 22:23:13 -0800 (PST) Date: Mon, 26 Jan 2026 22:23:02 -0800 In-Reply-To: <20260127062302.544809-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: <20260127062302.544809-1-irogers@google.com> X-Mailer: git-send-email 2.52.0.457.g6b5491de43-goog Message-ID: <20260127062302.544809-4-irogers@google.com> Subject: [PATCH v9 3/3] perf llvm: Mangle libperf-llvm.so function names 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" For a function like llvm_addr2line having the libperf-llvm.so exported symbol named llvm_addr2line meant that the perf llvm_addr2line could sometimes erroneously be returned. This led to infinite recursion and eventual stack overflow. To avoid this conflict add a new BUILDING_PERF_LLVMSO when libperf-llvm.so is being built and use it to alter the behavior of MANGLE_PERF_LLVM_API, a macro that prefixes the name when libperf-llvm.so is being built. The prefixed named avoids the name collision. Signed-off-by: Ian Rogers --- tools/perf/Makefile.perf | 3 ++- tools/perf/util/llvm-c-helpers.cpp | 29 ++++++++++++++++++----------- tools/perf/util/llvm-c-helpers.h | 24 ++++++++++++++++-------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 30e16a564d60..dafe5a6b8c0f 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -996,7 +996,8 @@ $(LIBSYMBOL)-clean: $(Q)$(RM) -r -- $(LIBSYMBOL_OUTPUT) =20 ifdef LIBLLVM_DYNAMIC -LIBPERF_LLVM_CXXFLAGS :=3D $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXF= LAGS)) -DHAVE_LIBLLVM_SUPPORT +LIBPERF_LLVM_CXXFLAGS :=3D $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXF= LAGS)) +LIBPERF_LLVM_CXXFLAGS +=3D -DHAVE_LIBLLVM_SUPPORT -DBUILDING_PERF_LLVMSO LIBPERF_LLVM_LIBS =3D -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) -lstdc= ++ =20 $(OUTPUT)$(LIBPERF_LLVM): util/llvm-c-helpers.cpp diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-he= lpers.cpp index 5a6f76e6b705..8cea380be5c2 100644 --- a/tools/perf/util/llvm-c-helpers.cpp +++ b/tools/perf/util/llvm-c-helpers.cpp @@ -99,10 +99,12 @@ static int extract_file_and_line(const DILineInfo &line= _info, char **file, #endif =20 extern "C" -int llvm_addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_u= nused, - char **file __maybe_unused, unsigned int *line __maybe_unused, - bool unwind_inlines __maybe_unused, - llvm_a2l_frame **inline_frames __maybe_unused) +int MANGLE_PERF_LLVM_API(llvm_addr2line)(const char *dso_name __maybe_unus= ed, + u64 addr __maybe_unused, + char **file __maybe_unused, + unsigned int *line __maybe_unused, + bool unwind_inlines __maybe_unuse= d, + llvm_a2l_frame **inline_frames __= maybe_unused) { #if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) LLVMSymbolizer *symbolizer =3D get_symbolizer(); @@ -177,7 +179,8 @@ int llvm_addr2line(const char *dso_name __maybe_unused,= u64 addr __maybe_unused, if (!handle) return 0; =20 - fn =3D reinterpret_cast(dlsym(handle, "llvm_addr2line")); + fn =3D reinterpret_cast( + dlsym(handle, MANGLE_PERF_LLVM_API_STR(llvm_addr2line))); if (!fn) pr_debug("dlsym failed for llvm_addr2line\n"); fn_init =3D true; @@ -215,8 +218,9 @@ make_symbol_relative_string(struct dso *dso, const char= *sym_name, #endif =20 extern "C" -char *llvm_name_for_code(struct dso *dso __maybe_unused, const char *dso_n= ame __maybe_unused, - u64 addr __maybe_unused) +char *MANGLE_PERF_LLVM_API(llvm_name_for_code)(struct dso *dso __maybe_unu= sed, + const char *dso_name __maybe_unused, + u64 addr __maybe_unused) { #if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) LLVMSymbolizer *symbolizer =3D get_symbolizer(); @@ -242,7 +246,8 @@ char *llvm_name_for_code(struct dso *dso __maybe_unused= , const char *dso_name __ if (!handle) return NULL; =20 - fn =3D reinterpret_cast(dlsym(handle, "llvm_name_for_code"= )); + fn =3D reinterpret_cast( + dlsym(handle, MANGLE_PERF_LLVM_API_STR(llvm_name_for_code))); if (!fn) pr_debug("dlsym failed for llvm_name_for_code\n"); fn_init =3D true; @@ -256,8 +261,9 @@ char *llvm_name_for_code(struct dso *dso __maybe_unused= , const char *dso_name __ } =20 extern "C" -char *llvm_name_for_data(struct dso *dso __maybe_unused, const char *dso_n= ame __maybe_unused, - u64 addr __maybe_unused) +char *MANGLE_PERF_LLVM_API(llvm_name_for_data)(struct dso *dso __maybe_unu= sed, + const char *dso_name __maybe_unused, + u64 addr __maybe_unused) { #if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC) LLVMSymbolizer *symbolizer =3D get_symbolizer(); @@ -283,7 +289,8 @@ char *llvm_name_for_data(struct dso *dso __maybe_unused= , const char *dso_name __ if (!handle) return NULL; =20 - fn =3D reinterpret_cast(dlsym(handle, "llvm_name_for_data"= )); + fn =3D reinterpret_cast( + dlsym(handle, MANGLE_PERF_LLVM_API_STR(llvm_name_for_data))); if (!fn) pr_debug("dlsym failed for llvm_name_for_data\n"); fn_init =3D true; diff --git a/tools/perf/util/llvm-c-helpers.h b/tools/perf/util/llvm-c-help= ers.h index d2b99637a28a..cfcfd540cdae 100644 --- a/tools/perf/util/llvm-c-helpers.h +++ b/tools/perf/util/llvm-c-helpers.h @@ -13,6 +13,14 @@ extern "C" { #endif =20 +/* Support name mangling so that libperf_llvm.so's names don't match those= in perf. */ +#ifdef BUILDING_PERF_LLVMSO +#define MANGLE_PERF_LLVM_API(x) PERF_LLVM_SO_ ## x +#else +#define MANGLE_PERF_LLVM_API(x) x +#endif +#define MANGLE_PERF_LLVM_API_STR(x) "PERF_LLVM_SO_" #x + struct dso; =20 struct llvm_a2l_frame { @@ -37,12 +45,12 @@ struct llvm_a2l_frame { * a newly allocated array with that length. The caller is then responsible * for freeing both the strings and the array itself. */ -int llvm_addr2line(const char* dso_name, - u64 addr, - char** file, - unsigned int* line, - bool unwind_inlines, - struct llvm_a2l_frame** inline_frames); +int MANGLE_PERF_LLVM_API(llvm_addr2line)(const char *dso_name, + u64 addr, + char **file, + unsigned int *line, + bool unwind_inlines, + struct llvm_a2l_frame **inline_frames); =20 /* * Simple symbolizers for addresses; will convert something like @@ -50,8 +58,8 @@ int llvm_addr2line(const char* dso_name, * * The returned value must be freed by the caller, with free(). */ -char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr); -char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr); +char *MANGLE_PERF_LLVM_API(llvm_name_for_code)(struct dso *dso, const char= *dso_name, u64 addr); +char *MANGLE_PERF_LLVM_API(llvm_name_for_data)(struct dso *dso, const char= *dso_name, u64 addr); =20 #ifdef __cplusplus } --=20 2.52.0.457.g6b5491de43-goog