lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAP-5=fV9LoY4AQjTVC=GNTOYOc_PGBwdP+HC=hMPsY=0ZqpExw@mail.gmail.com>
Date: Fri, 24 Jan 2025 17:21:45 -0800
From: Ian Rogers <irogers@...gle.com>
To: Namhyung Kim <namhyung@...nel.org>
Cc: Peter Zijlstra <peterz@...radead.org>, Ingo Molnar <mingo@...hat.com>, 
	Arnaldo Carvalho de Melo <acme@...nel.org>, Mark Rutland <mark.rutland@....com>, 
	Alexander Shishkin <alexander.shishkin@...ux.intel.com>, Jiri Olsa <jolsa@...nel.org>, 
	Adrian Hunter <adrian.hunter@...el.com>, Kan Liang <kan.liang@...ux.intel.com>, 
	Nathan Chancellor <nathan@...nel.org>, Nick Desaulniers <ndesaulniers@...gle.com>, 
	Bill Wendling <morbo@...gle.com>, Justin Stitt <justinstitt@...gle.com>, 
	Aditya Gupta <adityag@...ux.ibm.com>, "Steinar H. Gunderson" <sesse@...gle.com>, 
	Charlie Jenkins <charlie@...osinc.com>, Changbin Du <changbin.du@...wei.com>, 
	"Masami Hiramatsu (Google)" <mhiramat@...nel.org>, James Clark <james.clark@...aro.org>, 
	Kajol Jain <kjain@...ux.ibm.com>, Athira Rajeev <atrajeev@...ux.vnet.ibm.com>, 
	Li Huafei <lihuafei1@...wei.com>, Dmitry Vyukov <dvyukov@...gle.com>, 
	Andi Kleen <ak@...ux.intel.com>, Chaitanya S Prakash <chaitanyas.prakash@....com>, 
	linux-kernel@...r.kernel.org, linux-perf-users@...r.kernel.org, 
	llvm@...ts.linux.dev, Song Liu <song@...nel.org>, bpf@...r.kernel.org
Subject: Re: [PATCH v3 07/18] perf llvm: Support for dlopen-ing libLLVM.so

On Fri, Jan 24, 2025 at 2:54 PM Namhyung Kim <namhyung@...nel.org> wrote:
>
> On Wed, Jan 22, 2025 at 09:42:57AM -0800, Ian Rogers wrote:
> > 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, build that C++ code
> > against libLLVM.so as a separate shared object, and support dynamic
> > loading of it. This build option is enabled with LIBLLVM_DYNAMIC=1
>
> You mean dlopen libllvm is supported only if this build option is used,
> right?  I'm afraid that would also make others hard to use this feature.
> Anyway I think _DYNAMIC is more about static link vs. dynamic link.
> Maybe is LIBLLVM_DLOPEN=1 a little better?  Also please add a
> description to Makefile.perf when you add a build option.

Agreed on adding a comment and that LIBLLVM_DYNAMIC name perhaps isn't the best.

The situation with the series is that:
1) if libllvm is detected then HAVE_LIBLLVM_SUPPORT is enabled and we
link with LLVM - this matches behavior in the tree currently;
2) if libllvm isn't detected (or NO_LIBLLVM=1 is passed to the build)
then we use dlopen for the disassembler in libLLVM.so and for
addr2line in libperf-llvm.so.

We need an option to build libperf-llvm.so and that is what setting
LIBLLVM_DYNAMIC does. We can't determine it by HAVE_LIBLLVM_SUPPORT as
that will link against libllvm and not use dlopen. We can't determine
it by not HAVE_LIBLLVM_SUPPORT, as that indicates dlopen is used but
also that libllvm wasn't feature detected.

So NO_LIBLLVM=1 is saying don't link with libllvm, LIBLLVM_DYNAMIC=1
is saying, build libperf-llvm.so. Things were done this way to be
compatible with existing build flag behavior.

> >
> > Signed-off-by: Ian Rogers <irogers@...gle.com>
> > ---
> >  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             | 271 +++++++++++++++++++++++++----
> >  6 files changed, 386 insertions(+), 38 deletions(-)
> >
> > diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
> > index cd773fbbc176..5c2814acc5d5 100644
> > --- a/tools/perf/Makefile.config
> > +++ b/tools/perf/Makefile.config
> > @@ -963,6 +963,19 @@ ifndef NO_LIBLLVM
> >      NO_LIBLLVM := 1
> >    endif
> >  endif
> > +ifdef LIBLLVM_DYNAMIC
> > +  ifndef NO_LIBLLVM
> > +    $(error LIBLLVM_DYNAMIC should be used with NO_LIBLLVM)
>
> Hmm.. it doesn't seem reasonable to use these two options together.
> Maybe you could force NO_LIBLLVM=1 when LIBLLVM_DYNAMIC is used.
>
>
> > +  endif
> > +  $(call feature_check,llvm-perf)
> > +  ifneq ($(feature-llvm-perf), 1)
> > +    $(warning LIBLLVM_DYNAMIC requires libLLVM.so which wasn't feature detected)
>
> Huh?  It's not clear whether you need libLLVM.so or not here.  Can you
> proceed without it?  Why isn't it an error?

It should be an error but I lowered it to a warning to work around
issues with build-test in common with most user errors in
Makefile.config just being warnings.

> Also it looks like it's against the original purpose.  I think you
> wanted dlopen because the library is not available at build time.  But
> now you need it in the build script?

No, the dynamic is saying we want to build libperf-llvm.so for the
sake of addr2line. We want libllvm for that end, we expect regularly
linked llvm support to be disabled.

> > +  endif
> > +  CFLAGS += -DHAVE_LIBLLVM_DYNAMIC
> > +  CFLAGS += $(shell $(LLVM_CONFIG) --cflags)
> > +  CXXFLAGS += -DHAVE_LIBLLVM_DYNAMIC
> > +  CXXFLAGS += $(shell $(LLVM_CONFIG) --cxxflags)
> > +endif
> >
> >  ifndef NO_DEMANGLE
> >    $(call feature_check,cxa-demangle)
> > diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
> > index 55d6ce9ea52f..eae77f6af59d 100644
> > --- a/tools/perf/Makefile.perf
> > +++ b/tools/perf/Makefile.perf
> > @@ -456,6 +456,12 @@ ifndef NO_JVMTI
> >  PROGRAMS += $(OUTPUT)$(LIBJVMTI)
> >  endif
> >
> > +LIBPERF_LLVM = libperf-llvm.so
> > +
> > +ifdef LIBLLVM_DYNAMIC
> > +PROGRAMS += $(OUTPUT)$(LIBPERF_LLVM)
> > +endif
> > +
> >  DLFILTERS := dlfilter-test-api-v0.so dlfilter-test-api-v2.so dlfilter-show-cycles.so
> >  DLFILTERS := $(patsubst %,$(OUTPUT)dlfilters/%,$(DLFILTERS))
> >
> > @@ -1019,6 +1025,16 @@ $(LIBSYMBOL)-clean:
> >       $(call QUIET_CLEAN, libsymbol)
> >       $(Q)$(RM) -r -- $(LIBSYMBOL_OUTPUT)
> >
> > +ifdef LIBLLVM_DYNAMIC
> > +LIBPERF_LLVM_CXXFLAGS := $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXFLAGS)) -DHAVE_LIBLLVM_SUPPORT
> > +LIBPERF_LLVM_LIBS = -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)'
> > @@ -1120,6 +1136,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)'
> > @@ -1301,7 +1322,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-vdso32 \
> > -             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 \
> >               $(OUTPUT)common-cmds.h 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 0ee94caf9ec1..44d76eacce49 100644
> > --- a/tools/perf/tests/make
> > +++ b/tools/perf/tests/make
> > @@ -93,6 +93,7 @@ make_libbpf_dynamic := LIBBPF_DYNAMIC=1
> >  make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1
> >  make_no_libcrypto   := NO_LIBCRYPTO=1
> >  make_no_libllvm     := NO_LIBLLVM=1
> > +make_libllvm_dynamic := NO_LIBLLVM=1 LIBLLVM_DYNAMIC=1
> >  make_with_babeltrace:= LIBBABELTRACE=1
> >  make_with_coresight := CORESIGHT=1
> >  make_no_sdt      := NO_SDT=1
> > @@ -162,6 +163,7 @@ run += make_no_libbpf
> >  run += make_no_libbpf_DEBUG
> >  run += make_no_libcrypto
> >  run += make_no_libllvm
> > +run += make_libllvm_dynamic
> >  run += make_no_sdt
> >  run += make_no_syscall_tbl
> >  run += make_with_babeltrace
> > diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> > index 6fe0b5882c97..eb00c599e179 100644
> > --- a/tools/perf/util/Build
> > +++ b/tools/perf/util/Build
> > @@ -27,6 +27,7 @@ perf-util-y += find_bit.o
> >  perf-util-y += get_current_dir_name.o
> >  perf-util-y += levenshtein.o
> >  perf-util-y += llvm.o
> > +perf-util-y += llvm-c-helpers.o
> >  perf-util-y += mmap.o
> >  perf-util-y += memswap.o
> >  perf-util-y += parse-events.o
> > @@ -239,7 +240,6 @@ perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o
> >  perf-util-y += demangle-ocaml.o
> >  perf-util-y += demangle-java.o
> >  perf-util-y += demangle-rust.o
> > -perf-util-$(CONFIG_LIBLLVM) += llvm-c-helpers.o
> >
> >  ifdef CONFIG_JITDUMP
> >  perf-util-$(CONFIG_LIBELF) += jitdump.o
> > diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-helpers.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 <= 15 */
> >  #include <llvm/DebugInfo/Symbolize/Symbolize.h>
> >  #include <llvm/Support/TargetSelect.h>
> >  #pragma GCC diagnostic pop
> > +#endif
> >
> > +#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC)
> > +#include <dlfcn.h>
> > +#endif
> >  #include <inttypes.h>
> >  #include <stdio.h>
> >  #include <sys/types.h>
> >  #include <linux/compiler.h>
> >  extern "C" {
> > +#include "debug.h"
> >  #include <linux/zalloc.h>
> >  }
> >  #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);
> >
> > +#ifdef HAVE_LIBLLVM_SUPPORT
> >  using namespace llvm;
> >  using llvm::symbolize::LLVMSymbolizer;
> > +#endif
> > +
> > +#if !defined(HAVE_LIBLLVM_SUPPORT) && defined(HAVE_LIBLLVM_DYNAMIC)
>
> Like I said, it'd be simpler if you could make HAVE_LIBLLVM_SUPPORT and
> HAVE_LIBLLVM_DYNAMIC (or _DLOPEN) mutually exclusive.
>
> And the same argument for the code organization.

I think you're missing the point that we (not strictly) need
libperf-llvm.so. The reason being is that the llvm-c-helpers depend on
the C++ LLVM API. The LLVM C API does a fine job of exposing the
disassembler which is why in llvm.c in the shim code without linking
against libllvm we can just dlsym into it. llvm-c-helpers is really a
simple LLVM addr2line implementation using the LLVM C++ headers.
Perhaps a better name than llvm-c-helpers.cpp would be
llvm-addr2line.cpp, maybe libperf-llvm.so should be
libperf-llvm-addr2line.so. llvm-c-helpers.cpp/llvm-addr2line.cpp is
either going to get called by perf's C code via an extern "C" API or
in the dlopen case the extern "C" API will do the dlopen/dlsym. The
function called by dlsym isn't all the LLVM C++ API shimmed, using
dlsym with C++ code at best isn't great. Instead the same
llvm-c-helpers.cpp/llvm-addr2line.cpp code is compiled as a .so
exposing the extern "C" API function with a slightly mangled name.
This means we have perf's C code calling libperf-llvm.so extern "C"
code, libperf-llvm.so is dynamically linked against libLLVM.so (not
dlopen-ing it as its all C++ code).

Thanks,
Ian

> > +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 = true;
> > +             dll_handle = dlopen("libperf-llvm.so", RTLD_LAZY);
> > +             if (!dll_handle)
> > +                     pr_debug("dlopen failed for libperf-llvm.so\n");
> > +     }
> > +     return dll_handle;
> > +}
> > +#endif
> >
> >  /*
> >   * Allocate a static LLVMSymbolizer, which will live to the end of the program.
> >   * Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not need
> >   * to store anything in the dso struct.
> >   */
> > +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
> >  static LLVMSymbolizer *get_symbolizer()
> >  {
> >       static LLVMSymbolizer *instance = nullptr;
> > @@ -49,8 +74,10 @@ static LLVMSymbolizer *get_symbolizer()
> >       }
> >       return instance;
> >  }
> > +#endif
> >
> >  /* 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 = line_info.Line;
> >       return 1;
> >  }
> > +#endif
> >
> >  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_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();
> >       object::SectionedAddress sectioned_addr = {
> >               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 = perf_llvm_c_helpers_dll_handle();
> > +
> > +             if (!handle)
> > +                     return 0;
> > +
> > +             fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_addr2line"));
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for llvm_addr2line\n");
> > +             fn_init = true;
> > +     }
> > +     if (!fn)
> > +             return 0;
> > +     return fn(dso_name, addr, file, line, unwind_inlines, inline_frames);
> > +#else
> > +     return 0;
> > +#endif
> >  }
> >
> > +#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 char *sym_name,
> >                       return strdup(sym_name);
> >       }
> >  }
> > +#endif
> >
> >  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_name __maybe_unused,
> > +                      u64 addr __maybe_unused)
> >  {
> > +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
> >       LLVMSymbolizer *symbolizer = get_symbolizer();
> >       object::SectionedAddress sectioned_addr = {
> >               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 = perf_llvm_c_helpers_dll_handle();
> > +
> > +             if (!handle)
> > +                     return NULL;
> > +
> > +             fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_name_for_code"));
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for llvm_name_for_code\n");
> > +             fn_init = true;
> > +     }
> > +     if (!fn)
> > +             return NULL;
> > +     return fn(dso, dso_name, addr);
> > +#else
> > +     return 0;
> > +#endif
> >  }
> >
> >  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_name __maybe_unused,
> > +                      u64 addr __maybe_unused)
> >  {
> > +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
> >       LLVMSymbolizer *symbolizer = get_symbolizer();
> >       object::SectionedAddress sectioned_addr = {
> >               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 = perf_llvm_c_helpers_dll_handle();
> > +
> > +             if (!handle)
> > +                     return NULL;
> > +
> > +             fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_name_for_data"));
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for llvm_name_for_data\n");
> > +             fn_init = 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 ddc737194692..f6a8943b7c9d 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,17 +8,243 @@
> >  #include "namespaces.h"
> >  #include "srcline.h"
> >  #include "symbol.h"
> > +#include <dlfcn.h>
> >  #include <fcntl.h>
> > +#include <inttypes.h>
> >  #include <unistd.h>
> >  #include <linux/zalloc.h>
> >
> > -#ifdef HAVE_LIBLLVM_SUPPORT
> > -#include "llvm-c-helpers.h"
> > +#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
> >  #include <llvm-c/Disassembler.h>
> >  #include <llvm-c/Target.h>
> > +#else
> > +typedef void *LLVMDisasmContextRef;
> > +typedef int (*LLVMOpInfoCallback)(void *dis_info, uint64_t pc, uint64_t offset,
> > +                               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[] = {
> > +     "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 = true;
> > +             dll_handle = 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 = 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 as a symbol. */
> > +     static bool done_init;
> > +
> > +     if (done_init)
> > +             return;
> > +
> > +     for (size_t i = 0; i < ARRAY_SIZE(llvm_targets); i++) {
> > +             void (*fn)(void) = perf_llvm_dll_fun("LLVMInitialize%sTargetInfo",
> > +                                                  llvm_targets[i]);
> > +
> > +             if (!fn)
> > +                     continue;
> > +             fn();
> > +     }
> > +     done_init = 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 = 0; i < ARRAY_SIZE(llvm_targets); i++) {
> > +             void (*fn)(void) = perf_llvm_dll_fun("LLVMInitialize%sTargetMC",
> > +                                                  llvm_targets[i]);
> > +
> > +             if (!fn)
> > +                     continue;
> > +             fn();
> > +     }
> > +     done_init = 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 = 0; i < ARRAY_SIZE(llvm_targets); i++) {
> > +             void (*fn)(void) = perf_llvm_dll_fun("LLVMInitialize%sDisassembler",
> > +                                                  llvm_targets[i]);
> > +
> > +             if (!fn)
> > +                     continue;
> > +             fn();
> > +     }
> > +     done_init = 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, symbol_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 = dlsym(perf_llvm_dll_handle(), "LLVMCreateDisasm");
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for LLVMCreateDisasm\n");
> > +             fn_init = 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 = dlsym(perf_llvm_dll_handle(), "LLVMSetDisasmOptions");
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for LLVMSetDisasmOptions\n");
> > +             fn_init = true;
> > +     }
> > +     if (!fn)
> > +             return 0;
> > +     return fn(context, options);
> > +#endif
> > +}
> > +
> > +static size_t perf_LLVMDisasmInstruction(LLVMDisasmContextRef context, uint8_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 = dlsym(perf_llvm_dll_handle(), "LLVMDisasmInstruction");
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for LLVMDisasmInstruction\n");
> > +             fn_init = 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 = dlsym(perf_llvm_dll_handle(), "LLVMDisasmDispose");
> > +             if (!fn)
> > +                     pr_debug("dlsym failed for LLVMDisasmDispose\n");
> > +             fn_init = true;
> > +     }
> > +     if (!fn)
> > +             return;
> > +     fn(context);
> >  #endif
> > +}
> > +
> >
> > -#ifdef HAVE_LIBLLVM_SUPPORT
> >  static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames,
> >                                   int num_frames)
> >  {
> > @@ -29,14 +256,12 @@ static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames,
> >               zfree(&inline_frames);
> >       }
> >  }
> > -#endif
> >
> >  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 = NULL;
> >       int num_frames = llvm_addr2line(dso_name, addr, file, line,
> >                                       node && unwind_inlines, &inline_frames);
> > @@ -64,9 +289,6 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
> >       free_llvm_inline_frames(inline_frames, num_frames);
> >
> >       return num_frames;
> > -#else
> > -     return -1;
> > -#endif
> >  }
> >
> >  void dso__free_a2l_llvm(struct dso *dso __maybe_unused)
> > @@ -75,7 +297,6 @@ void dso__free_a2l_llvm(struct dso *dso __maybe_unused)
> >  }
> >
> >
> > -#if defined(HAVE_LIBLLVM_SUPPORT)
> >  struct find_file_offset_data {
> >       u64 ip;
> >       u64 offset;
> > @@ -139,7 +360,6 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
> >       free(buf);
> >       return NULL;
> >  }
> > -#endif
> >
> >  /*
> >   * Whenever LLVM wants to resolve an address into a symbol, it calls this
> > @@ -149,7 +369,6 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
> >   * should add some textual annotation for after the instruction. The caller
> >   * will use this information to add the actual annotation.
> >   */
> > -#ifdef HAVE_LIBLLVM_SUPPORT
> >  struct symbol_lookup_storage {
> >       u64 branch_addr;
> >       u64 pcrel_load_addr;
> > @@ -170,12 +389,10 @@ symbol_lookup_callback(void *disinfo, uint64_t value,
> >       *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
> >       return NULL;
> >  }
> > -#endif
> >
> >  int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
> >                            struct annotate_args *args __maybe_unused)
> >  {
> > -#ifdef HAVE_LIBLLVM_SUPPORT
> >       struct annotation *notes = symbol__annotation(sym);
> >       struct map *map = args->ms.map;
> >       struct dso *dso = map__dso(map);
> > @@ -197,9 +414,9 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
> >       if (args->options->objdump_path)
> >               return -1;
> >
> > -     LLVMInitializeAllTargetInfos();
> > -     LLVMInitializeAllTargetMCs();
> > -     LLVMInitializeAllDisassemblers();
> > +     perf_LLVMInitializeAllTargetInfos();
> > +     perf_LLVMInitializeAllTargetMCs();
> > +     perf_LLVMInitializeAllDisassemblers();
> >
> >       buf = read_symbol(filename, map, sym, &len, &is_64bit);
> >       if (buf == NULL)
> > @@ -215,15 +432,14 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
> >                         args->arch->name);
> >       }
> >
> > -     disasm = LLVMCreateDisasm(triplet, &storage, 0, NULL,
> > -                               symbol_lookup_callback);
> > +     disasm = perf_LLVMCreateDisasm(triplet, &storage, 0, NULL,
> > +                                    symbol_lookup_callback);
> >       if (disasm == NULL)
> >               goto err;
> >
> >       if (args->options->disassembler_style &&
> >           !strcmp(args->options->disassembler_style, "intel"))
> > -             LLVMSetDisasmOptions(disasm,
> > -                                  LLVMDisassembler_Option_AsmPrinterVariant);
> > +             perf_LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_AsmPrinterVariant);
> >
> >       /*
> >        * This needs to be set after AsmPrinterVariant, due to a bug in LLVM;
> > @@ -231,7 +447,7 @@ int symbol__disassemble_llvm(const char *filename, struct 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);
> >
> >       /* add the function address and name */
> >       scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
> > @@ -256,9 +472,9 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
> >               storage.branch_addr = 0;
> >               storage.pcrel_load_addr = 0;
> >
> > -             ins_len = LLVMDisasmInstruction(disasm, buf + offset,
> > -                                             len - offset, pc,
> > -                                             disasm_buf, sizeof(disasm_buf));
> > +             ins_len = perf_LLVMDisasmInstruction(disasm, buf + offset,
> > +                                                  len - offset, pc,
> > +                                                  disasm_buf, sizeof(disasm_buf));
> >               if (ins_len == 0)
> >                       goto err;
> >               disasm_len = strlen(disasm_buf);
> > @@ -314,13 +530,8 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
> >       ret = 0;
> >
> >  err:
> > -     LLVMDisasmDispose(disasm);
> > +     perf_LLVMDisasmDispose(disasm);
> >       free(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
> >  }
> > --
> > 2.48.1.262.g85cc9f2d1e-goog
> >

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ