[PATCH v2 08/17] perf llvm: Mangle libperf-llvm.so function names

Ian Rogers
2025-01-22T06:24:54Z

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 <irogers@google.com>
---
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 eae77f6af59d..a2886abd4f02 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -1026,7 +1026,8 @@ $(LIBSYMBOL)-clean:
$(Q)$(RM) -r -- $(LIBSYMBOL_OUTPUT)

ifdef LIBLLVM_DYNAMIC
-LIBPERF_LLVM_CXXFLAGS := $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXFLAGS)) -DHAVE_LIBLLVM_SUPPORT
+LIBPERF_LLVM_CXXFLAGS := $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXFLAGS))
+LIBPERF_LLVM_CXXFLAGS += -DHAVE_LIBLLVM_SUPPORT -DBUILDING_PERF_LLVMSO
LIBPERF_LLVM_LIBS = -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) -lstdc++

$(OUTPUT)$(LIBPERF_LLVM): util/llvm-c-helpers.cpp
diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-helpers.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

extern "C"
-int llvm_addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused,
- 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_unused,
+ u64 addr __maybe_unused,
+ 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 = get_symbolizer();
@@ -177,7 +179,8 @@ int llvm_addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused,
if (!handle)
return 0;

- fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_addr2line"));
+ fn = reinterpret_cast<decltype(fn)>(
+ dlsym(handle, MANGLE_PERF_LLVM_API_STR(llvm_addr2line)));
if (!fn)
pr_debug("dlsym failed for llvm_addr2line\n");
fn_init = true;
@@ -215,8 +218,9 @@ make_symbol_relative_string(struct dso *dso, const char *sym_name,
#endif

extern "C"
-char *llvm_name_for_code(struct dso *dso __maybe_unused, const char *dso_name __maybe_unused,
- u64 addr __maybe_unused)
+char *MANGLE_PERF_LLVM_API(llvm_name_for_code)(struct dso *dso __maybe_unused,
+ const char *dso_name __maybe_unused,
+ u64 addr __maybe_unused)
{
#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
LLVMSymbolizer *symbolizer = get_symbolizer();
@@ -242,7 +246,8 @@ char *llvm_name_for_code(struct dso *dso __maybe_unused, const char *dso_name __
if (!handle)
return NULL;

- fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_name_for_code"));
+ fn = reinterpret_cast<decltype(fn)>(
+ 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 = true;
@@ -256,8 +261,9 @@ char *llvm_name_for_code(struct dso *dso __maybe_unused, const char *dso_name __
}

extern "C"
-char *llvm_name_for_data(struct dso *dso __maybe_unused, const char *dso_name __maybe_unused,
- u64 addr __maybe_unused)
+char *MANGLE_PERF_LLVM_API(llvm_name_for_data)(struct dso *dso __maybe_unused,
+ const char *dso_name __maybe_unused,
+ u64 addr __maybe_unused)
{
#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
LLVMSymbolizer *symbolizer = get_symbolizer();
@@ -283,7 +289,8 @@ char *llvm_name_for_data(struct dso *dso __maybe_unused, const char *dso_name __
if (!handle)
return NULL;

- fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_name_for_data"));
+ fn = reinterpret_cast<decltype(fn)>(
+ 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 = true;
diff --git a/tools/perf/util/llvm-c-helpers.h b/tools/perf/util/llvm-c-helpers.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

+/* 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;

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

/*
* 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);

#ifdef __cplusplus