From a42836f4e0be33005c72a514fb471388a258f3c2 Mon Sep 17 00:00:00 2001 From: Martin Bruse Date: Tue, 14 Oct 2025 13:23:00 +0000 Subject: [PATCH] [Windows] Fix plugin registry symbols not exported/linked with CLANG_LINK_CLANG_DYLIB When building LLVM with CLANG_LINK_CLANG_DYLIB=ON on Windows, external plugins cannot register through llvm::Registry due to two issues: 1. Static data members (Head, Tail) are filtered out during symbol export 2. Static methods (add_node(), begin()) are not linked into the executable This change fixes both issues: - Modified extract_symbols.py to recognize and export Registry::Head and Registry::Tail static members using pattern matching instead of filtering them out as non-function symbols. - Added code in driver.cpp that references Registry methods, forcing the linker to include these symbols in clang.exe even though they're not directly called by the driver. Fixes #163367 --- clang/tools/driver/driver.cpp | 19 +++++++++++++++++++ llvm/utils/extract_symbols.py | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index 7390d7d610ec0..855c8285e841c 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -55,6 +55,25 @@ #include #include +#if defined(CLANG_PLUGIN_SUPPORT) && defined(_WIN32) +#include "clang/Frontend/FrontendPluginRegistry.h" + +// Force plugin registry symbols into clang.exe on Windows so plugins can +// register. These methods exist in libraries but aren't linked by default +// because they're unreferenced. Taking their addresses forces the linker to +// include them. +namespace { +void ForcePluginRegistrySymbols() { + using PluginRegistry = llvm::Registry; + // Use volatile to prevent the compiler from optimizing away these references + volatile auto add_node_ptr = &PluginRegistry::add_node; + volatile auto begin_ptr = &PluginRegistry::begin; + (void)add_node_ptr; + (void)begin_ptr; +} +} // anonymous namespace +#endif + using namespace clang; using namespace clang::driver; using namespace llvm::opt; diff --git a/llvm/utils/extract_symbols.py b/llvm/utils/extract_symbols.py index 388723421d660..72f992f560c7f 100755 --- a/llvm/utils/extract_symbols.py +++ b/llvm/utils/extract_symbols.py @@ -105,6 +105,11 @@ def should_keep_microsoft_symbol(symbol, calling_convention_decoration): # Skip X86GenMnemonicTables functions, they are not exposed from llvm/include/. elif re.match(r"\?is[A-Z0-9]*@X86@llvm", symbol): return None + # Keep Registry::Head and Registry::Tail static members for plugin support. + # Pattern matches: ?Head@?$Registry@@llvm@@ or ?Tail@?$Registry@... + elif ("?$Registry@" in symbol and "@llvm@@" in symbol and + (symbol.startswith("?Head@") or symbol.startswith("?Tail@"))): + return symbol # Keep mangled llvm:: and clang:: function symbols. How we detect these is a # bit of a mess and imprecise, but that avoids having to completely demangle # the symbol name. The outermost namespace is at the end of the identifier