[llvm] [LLVM] Support EXPORT_SYMBOLS_FOR_PLUGINS with shared or dylib builds (PR #191424)

Tomohiro Kashiwada via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 10 07:18:17 PDT 2026


https://github.com/kikairoya created https://github.com/llvm/llvm-project/pull/191424

Resolve the incompatibilities of EXPORT_SYMBOLS_FOR_PLUGINS with `-DBUILD_SHARED_LIBS=ON` or `-DLLVM_LINK_LLVM_DYLIB=ON`:

- Static libraries linked to the target may be empty:
  Added a checking if the target link any static libraries.
- `export_executable_symbols` may run before target's dependencies are fully registered:
  Defer scanning the target's dependencies to just before finishing CMaking.

>From c9a0dce364c1b72af704600065bfd32fd09b0cd3 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Sun, 9 Nov 2025 21:00:27 +0900
Subject: [PATCH 1/3] always specify `.exe.a` for the suffix of import library

needs for LLVM_BUILD_LLVM_DYLIB
---
 llvm/cmake/modules/AddLLVM.cmake | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake
index 0730ba2f529ed..a24afb729a8ba 100644
--- a/llvm/cmake/modules/AddLLVM.cmake
+++ b/llvm/cmake/modules/AddLLVM.cmake
@@ -1217,6 +1217,14 @@ macro(add_llvm_executable name)
     add_llvm_symbol_exports( ${name} ${LLVM_EXPORTED_SYMBOL_FILE} )
   endif(LLVM_EXPORTED_SYMBOL_FILE)
 
+  # The default import library suffix that cmake uses for cygwin/mingw is
+  # ".dll.a", but for clang.exe that causes a collision with libclang.dll,
+  # where the import libraries of both get named libclang.dll.a. Use a suffix
+  # of ".exe.a" to avoid this.
+  if(CYGWIN OR MINGW)
+    set_target_properties(${name} PROPERTIES IMPORT_SUFFIX ".exe.a")
+  endif()
+
   if (DEFINED LLVM_ENABLE_EXPORTED_SYMBOLS_IN_EXECUTABLES AND
       NOT LLVM_ENABLE_EXPORTED_SYMBOLS_IN_EXECUTABLES AND
       NOT ARG_EXPORT_SYMBOLS)
@@ -1517,13 +1525,6 @@ function(export_executable_symbols target)
     # transitive link against only the libraries whose symbols
     # we aren't exporting.
     set_target_properties(${target} PROPERTIES INTERFACE_LINK_LIBRARIES "${other_libs}")
-    # The default import library suffix that cmake uses for cygwin/mingw is
-    # ".dll.a", but for clang.exe that causes a collision with libclang.dll,
-    # where the import libraries of both get named libclang.dll.a. Use a suffix
-    # of ".exe.a" to avoid this.
-    if(CYGWIN OR MINGW)
-      set_target_properties(${target} PROPERTIES IMPORT_SUFFIX ".exe.a")
-    endif()
   elseif(NOT (WIN32 OR CYGWIN))
     # On Windows auto-exporting everything doesn't work because of the limit on
     # the size of the exported symbol table, but on other platforms we can do

>From f646b5ac08aea08d96b224c293e8cefc16291f21 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Wed, 17 Dec 2025 19:02:44 +0900
Subject: [PATCH 2/3] [LLVM] Support EXPORT_SYMBOLS_FOR_PLUGINS with shared or
 dylib builds

Resolve the incompatibilities of EXPORT_SYMBOLS_FOR_PLUGINS with
`-DBUILD_SHARED_LIBS=ON` or `-DLLVM_LINK_LLVM_DYLIB=ON`:

- Static libraries linked to the target may be empty:
  Added a checking if the target link any static libraries.
- `export_executable_symbols` may run before target's dependencies are fully registered:
  Defer scanning the target's dependencies to just before finishing CMaking.
---
 llvm/cmake/modules/AddLLVM.cmake | 114 ++++++++++++++++++-------------
 1 file changed, 65 insertions(+), 49 deletions(-)

diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake
index a24afb729a8ba..c283ad8edaa9c 100644
--- a/llvm/cmake/modules/AddLLVM.cmake
+++ b/llvm/cmake/modules/AddLLVM.cmake
@@ -1456,52 +1456,44 @@ function(process_llvm_pass_plugins)
   endif()
 endfunction()
 
-function(export_executable_symbols target)
-  if (LLVM_EXPORTED_SYMBOL_FILE)
-    # The symbol file should contain the symbols we want the executable to
-    # export
-    set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
-  elseif (LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
-    # Extract the symbols to export from the static libraries that the
-    # executable links against.
-    set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
-    set(exported_symbol_file ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${target}.symbols)
-    # We need to consider not just the direct link dependencies, but also the
-    # transitive link dependencies. Do this by starting with the set of direct
-    # dependencies, then the dependencies of those dependencies, and so on.
-    get_target_property(new_libs ${target} LINK_LIBRARIES)
-    set(link_libs ${new_libs})
-    while(NOT "${new_libs}" STREQUAL "")
-      foreach(lib ${new_libs})
-        if(TARGET ${lib})
-          # If this is a ALIAS target, continue with its aliasee instead.
-          get_target_property(aliased_lib ${lib} ALIASED_TARGET)
-          if(aliased_lib)
-             set(new_libs ${lib_aliased_target})
-             list(APPEND newer_libs ${aliased_lib})
-             continue()
-          endif()
+function(do_export_executable_symbols_for_plugins target exported_symbol_file)
+  # We need to consider not just the direct link dependencies, but also the
+  # transitive link dependencies. Do this by starting with the set of direct
+  # dependencies, then the dependencies of those dependencies, and so on.
+  get_target_property(new_libs ${target} LINK_LIBRARIES)
+  set(link_libs ${new_libs})
+  while(NOT "${new_libs}" STREQUAL "")
+    foreach(lib ${new_libs})
+      if(TARGET ${lib})
+        # If this is a ALIAS target, continue with its aliasee instead.
+        get_target_property(aliased_lib ${lib} ALIASED_TARGET)
+        if(aliased_lib)
+          set(new_libs ${lib_aliased_target})
+          list(APPEND newer_libs ${aliased_lib})
+          continue()
+        endif()
 
-          get_target_property(lib_type ${lib} TYPE)
-          if("${lib_type}" STREQUAL "STATIC_LIBRARY")
-            list(APPEND static_libs ${lib})
-          else()
-            list(APPEND other_libs ${lib})
-          endif()
-          get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES)
-          foreach(transitive_lib ${transitive_libs})
-            if(TARGET ${transitive_lib} AND NOT ${transitive_lib} IN_LIST link_libs)
-              list(APPEND newer_libs ${transitive_lib})
-              list(APPEND link_libs ${transitive_lib})
-            endif()
-          endforeach(transitive_lib)
+        get_target_property(lib_type ${lib} TYPE)
+        if("${lib_type}" STREQUAL "STATIC_LIBRARY")
+          list(APPEND static_libs ${lib})
+        else()
+          list(APPEND other_libs ${lib})
         endif()
-      endforeach(lib)
-      set(new_libs ${newer_libs})
-      set(newer_libs "")
-    endwhile()
-    list(REMOVE_DUPLICATES static_libs)
-    if (MSVC)
+        get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES)
+        foreach(transitive_lib ${transitive_libs})
+          if(TARGET ${transitive_lib} AND NOT ${transitive_lib} IN_LIST link_libs)
+            list(APPEND newer_libs ${transitive_lib})
+            list(APPEND link_libs ${transitive_lib})
+          endif()
+        endforeach(transitive_lib)
+      endif()
+    endforeach(lib)
+    set(new_libs ${newer_libs})
+    set(newer_libs "")
+  endwhile()
+  list(REMOVE_DUPLICATES static_libs)
+  if(static_libs)
+    if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
       set(mangling microsoft)
     else()
       set(mangling itanium)
@@ -1520,11 +1512,35 @@ function(export_executable_symbols target)
                          ${static_libs} ${llvm_nm_target} ${llvm_readobj_target}
                        VERBATIM
                        COMMENT "Generating export list for ${target}")
-    add_llvm_symbol_exports( ${target} ${exported_symbol_file} )
-    # If something links against this executable then we want a
-    # transitive link against only the libraries whose symbols
-    # we aren't exporting.
-    set_target_properties(${target} PROPERTIES INTERFACE_LINK_LIBRARIES "${other_libs}")
+  else()
+    # When shared or dylib builds, static_libs can be empty.
+    file(GENERATE OUTPUT ${exported_symbol_file} CONTENT "")
+  endif()
+  add_llvm_symbol_exports( ${target} ${exported_symbol_file} )
+  # If something links against this executable then we want a
+  # transitive link against only the libraries whose symbols
+  # we aren't exporting.
+  set_target_properties(${target} PROPERTIES INTERFACE_LINK_LIBRARIES "${other_libs}")
+endfunction()
+
+function(export_executable_symbols target)
+  if (LLVM_EXPORTED_SYMBOL_FILE)
+    # The symbol file should contain the symbols we want the executable to
+    # export
+    set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
+  elseif (LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
+    # Extract the symbols to export from the static libraries that the
+    # executable links against.
+    set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
+    set(exported_symbol_file ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${target}.symbols)
+    # Defer the actual process of exporting since dependants might not
+    # registered yet; e.g. exporting from `clang` is called before
+    # `clang-cpp`(clang-shlib) is registered.
+    cmake_language(EVAL CODE "
+      cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR}
+        CALL do_export_executable_symbols_for_plugins [[${target}]] [[${exported_symbol_file}]]
+      )
+    ")
   elseif(NOT (WIN32 OR CYGWIN))
     # On Windows auto-exporting everything doesn't work because of the limit on
     # the size of the exported symbol table, but on other platforms we can do

>From 76bc2b763e6784b82d459530c9108560daaa96f0 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Tue, 16 Dec 2025 22:36:50 +0900
Subject: [PATCH 3/3] make export_symbols_for_plugins default if dylib or
 shared

---
 llvm/cmake/modules/HandleLLVMOptions.cmake | 37 +++++++++++++---------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake
index 5f1d68762c038..ac8da1d4cd0e4 100644
--- a/llvm/cmake/modules/HandleLLVMOptions.cmake
+++ b/llvm/cmake/modules/HandleLLVMOptions.cmake
@@ -1469,13 +1469,6 @@ if(LLVM_ENABLE_FATLTO AND ((UNIX AND NOT APPLE) OR FUCHSIA))
   endif()
 endif()
 
-# Set an AIX default for LLVM_EXPORT_SYMBOLS_FOR_PLUGINS based on whether we are
-# doing dynamic linking (see below).
-set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_AIX_default OFF)
-if (NOT (BUILD_SHARED_LIBS OR LLVM_LINK_LLVM_DYLIB))
-  set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_AIX_default ON)
-endif()
-
 # This option makes utils/extract_symbols.py be used to determine the list of
 # symbols to export from LLVM tools. This is necessary when on AIX or when using
 # MSVC if you want to allow plugins. On AIX we don't show this option, and we
@@ -1483,19 +1476,33 @@ endif()
 # linking (due to incompatibility). With MSVC, note that the plugin has to
 # explicitly link against (exactly one) tool so we can't unilaterally turn on
 # LLVM_ENABLE_PLUGINS when it's enabled.
+set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_default OFF)
+set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_OPTION ON)
 if("${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
+  # Set an AIX default for LLVM_EXPORT_SYMBOLS_FOR_PLUGINS based on whether we are
+  # doing dynamic linking (see above).
+  if(NOT (BUILD_SHARED_LIBS OR LLVM_LINK_LLVM_DYLIB))
+    set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_default ON)
+  endif()
   set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_OPTION OFF)
-else()
-  set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_OPTION ON)
+elseif(WIN32 OR CYGWIN)
+  # Set a WIN32 default for LLVM_EXPORT_SYMBOLS_FOR_PLUGINS if we are
+  # doing dynamic linking.
+  if(BUILD_SHARED_LIBS OR LLVM_LINK_LLVM_DYLIB)
+    set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_default ON)
+  endif()
 endif()
 CMAKE_DEPENDENT_OPTION(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS
        "Export symbols from LLVM tools so that plugins can import them" OFF
-       "LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_OPTION" ${LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_AIX_default})
-if(BUILD_SHARED_LIBS AND LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
-  message(FATAL_ERROR "BUILD_SHARED_LIBS not compatible with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS")
-endif()
-if(LLVM_LINK_LLVM_DYLIB AND LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
-  message(FATAL_ERROR "LLVM_LINK_LLVM_DYLIB not compatible with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS")
+       "LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_OPTION" ${LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_default})
+
+if (CYGWIN)
+  # CMake sets CMAKE_EXE_EXPORTS_${lang}_FLAG to `-Wl,--export-all-symbols`
+  # for Cygwin. This is incompatible with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS
+  # tweaks, so unset them.
+  set(CMAKE_EXE_EXPORTS_ASM_FLAG "")
+  set(CMAKE_EXE_EXPORTS_C_FLAG "")
+  set(CMAKE_EXE_EXPORTS_CXX_FLAG "")
 endif()
 
 # By default we should enable LLVM_ENABLE_IDE only for multi-configuration



More information about the llvm-commits mailing list