Windows下用Clang编译LLVM Pass时出现未定义符号错误
问题:LLVM外部Pass编译链接报错(未定义符号)
尝试编写一个独立于LLVM源码树的简单LLVM Pass,但编译时出现大量lld-link未定义符号错误。以下是项目配置、代码及错误信息:
项目根目录CMakeLists.txt
cmake_minimum_required(VERSION 3.25) project(LLVM1Proj) # Set flags set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") # Include LLVM library find_package(LLVM REQUIRED CONFIG) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") include(AddLLVM) # Enable isolated passes set(LLVM_ENABLE_PLUGINS ON) separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS_LIST}) include_directories(${LLVM_INCLUDE_DIRS}) add_subdirectory("easypass") add_executable(LLVM1 "main.cpp") llvm_map_components_to_libnames(llvm_libs support core irreader) target_link_libraries(LLVM1 ${llvm_libs})
easypass子目录CMakeLists.txt
add_llvm_library(easypass MODULE easypass.cpp PLUGIN_TOOL opt )
easypass.cpp代码
#include "llvm/Pass.h" #include "llvm/IR/Function.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" namespace { class Easy : public llvm::FunctionPass { public: static char ID; Easy() : FunctionPass(ID) {} bool runOnFunction(llvm::Function &F) override { llvm::errs() << "Hello: "; llvm::errs().write_escaped(F.getName()) << '\n'; return false; } }; } char Easy::ID = 0; static llvm::RegisterPass<Easy> X("Easy", "Easy pass", false); static llvm::RegisterStandardPasses Y( llvm::PassManagerBuilder::EP_EarlyAsPossible, [](const llvm::PassManagerBuilder &Builder, llvm::legacy::PassManagerBase &PM) { PM.add(new Easy()); });
生成的编译命令
cmd.exe /C "cd . && C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\Llvm\x64\bin\clang.exe -fuse-ld=lld-link -nostartfiles -nostdlib -std=c++17 -O0 -g -Xclang -gcodeview -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -g -Xclang -gcodeview -Wl,--gc-sections -shared -o easypass\easypass.dll -Xlinker /MANIFEST:EMBED -Xlinker /implib:easypass\easypass.lib -Xlinker /pdb:easypass\easypass.pdb -Xlinker /version:0.0 easypass/CMakeFiles/easypass.dir/easypass.cpp.obj -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -loldnames && cd ."
编译错误信息
[build] lld-link: warning: ignoring unknown argument '--gc-sections' [build] lld-link: error: undefined symbol: public: static class llvm::PassRegistry * __cdecl llvm::PassRegistry::getPassRegistry(void) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\PassSupport.h:101 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: __cdecl llvm::RegisterPass<class `anonymous namespace'::Easy>::RegisterPass<class `anonymous namespace'::Easy>(class llvm::StringRef, class llvm::StringRef, bool, bool)) [build] [build] lld-link: error: undefined symbol: public: void __cdecl llvm::PassRegistry::registerPass(class llvm::PassInfo const &, bool) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\PassSupport.h:101 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: __cdecl llvm::RegisterPass<class `anonymous namespace'::Easy>::RegisterPass<class `anonymous namespace'::Easy>(class llvm::StringRef, class llvm::StringRef, bool, bool)) [build] [build] lld-link: error: undefined symbol: public: static int __cdecl llvm::PassManagerBuilder::addGlobalExtension(enum llvm::PassManagerBuilder::ExtensionPointTy, class std::function<void __cdecl(class llvm::PassManagerBuilder const &, class llvm::legacy::PassManagerBase &)>) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\Transforms\IPO\PassManagerBuilder.h:221 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: __cdecl llvm::RegisterStandardPasses::RegisterStandardPasses(enum llvm::PassManagerBuilder::ExtensionPointTy, class std::function<void __cdecl(class llvm::PassManagerBuilder const &, class llvm::legacy::PassManagerBase &)>)) [build] [build] lld-link: error: undefined symbol: public: static void __cdecl llvm::PassManagerBuilder::removeGlobalExtension(int) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\Transforms\IPO\PassManagerBuilder.h:228 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: __cdecl llvm::RegisterStandardPasses::~RegisterStandardPasses(void)) [build] [build] lld-link: error: undefined symbol: class llvm::raw_fd_ostream & __cdecl llvm::errs(void) [build] >>> referenced by C:\Development\Projects\LLVM1\easypass\easypass.cpp:19 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: virtual bool __cdecl `anonymous namespace'::Easy::runOnFunction(class llvm::Function &)) [build] >>> referenced by C:\Development\Projects\LLVM1\easypass\easypass.cpp:20 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: virtual bool __cdecl `anonymous namespace'::Easy::runOnFunction(class llvm::Function &)) [build] [build] lld-link: error: undefined symbol: public: class llvm::StringRef __cdecl llvm::Value::getName(void) const [build] >>> referenced by C:\Development\Projects\LLVM1\easypass\easypass.cpp:20 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: virtual bool __cdecl `anonymous namespace'::Easy::runOnFunction(class llvm::Function &)) [build] [build] lld-link: error: undefined symbol: public: class llvm::raw_ostream & __cdecl llvm::raw_ostream::write_escaped(class llvm::StringRef, bool) [build] >>> referenced by C:\Development\Projects\LLVM1\easypass\easypass.cpp:20 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: virtual bool __cdecl `anonymous namespace'::Easy::runOnFunction(class llvm::Function &)) [build] [build] lld-link: error: undefined symbol: public: virtual __cdecl llvm::Pass::~Pass(void) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\Pass.h:308 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: virtual __cdecl llvm::FunctionPass::~FunctionPass(void)) [build] [build] lld-link: error: undefined symbol: public: class llvm::raw_ostream & __cdecl llvm::raw_ostream::write(unsigned char) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\Support\raw_ostream.h:192 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: class llvm::raw_ostream & __cdecl llvm::raw_ostream::operator<<(char)) [build] [build] lld-link: error: undefined symbol: public: class llvm::raw_ostream & __cdecl llvm::raw_ostream::write(char const *, unsigned __int64) [build] >>> referenced by C:\Program Files (x86)\LLVM\include\llvm\Support\raw_ostream.h:217 [build] >>> easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(public: class llvm::raw_ostream & __cdecl llvm::raw_ostream::operator<<(class llvm::StringRef)) [build] [build] lld-link: error: undefined symbol: public: virtual class llvm::StringRef __cdecl llvm::Pass::getPassName(void) const [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual void __cdecl llvm::Pass::print(class llvm::raw_ostream &, class llvm::Module const *) const [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual class llvm::Pass * __cdecl llvm::FunctionPass::createPrinterPass(class llvm::raw_ostream &, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char>> const &) const [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual void __cdecl llvm::FunctionPass::assignPassManager(class llvm::PMStack &, enum llvm::PassManagerType) [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual void __cdecl llvm::Pass::preparePassManager(class llvm::PMStack &) [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual enum llvm::PassManagerType __cdecl llvm::FunctionPass::getPotentialPassManagerType(void) const [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual void __cdecl llvm::Pass::getAnalysisUsage(class llvm::AnalysisUsage &) const [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual void __cdecl llvm::Pass::releaseMemory(void) [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual void * __cdecl llvm::Pass::getAdjustedAnalysisPointer(void const *) [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: undefined symbol: public: virtual class llvm::ImmutablePass * __cdecl llvm::Pass::getAsImmutablePass(void) [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const `anonymous namespace'::Easy::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::FunctionPass::`vftable') [build] >>> referenced by easypass/CMakeFiles/easypass.dir/easypass.cpp.obj:(const llvm::Pass::`vftable') [build] [build] lld-link: error: too many errors emitted, stopping now (use /errorlimit:0 to see all errors) [build] clang: error: linker command failed with exit code 1
解决方案
问题核心是Pass模块未链接必要的LLVM组件库,同时CMake配置存在顺序错误,以下是修复步骤:
1. 修正根目录CMakeLists.txt
- 移除手动设置的
CMAKE_CXX_FLAGS:AddLLVM模块会自动配置符合LLVM要求的编译标准,手动设置可能导致版本不匹配 - 将
LLVM_ENABLE_PLUGINS移至find_package之前:该选项必须在查找LLVM包前设置才会生效
cmake_minimum_required(VERSION 3.25) project(LLVM1Proj) # 必须在find_package前启用插件支持 set(LLVM_ENABLE_PLUGINS ON) # Include LLVM library find_package(LLVM REQUIRED CONFIG) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") include(AddLLVM) separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS_LIST}) include_directories(${LLVM_INCLUDE_DIRS}) add_subdirectory("easypass") add_executable(LLVM1 "main.cpp") llvm_map_components_to_libnames(llvm_libs support core irreader) target_link_libraries(LLVM1 ${llvm_libs})
2. 修正easypass子目录CMakeLists.txt
添加Pass依赖的LLVM组件库,通过llvm_map_components_to_libnames映射所需库,再链接到模块:
llvm_map_components_to_libnames(llvm_libs core support passes ipo ) add_llvm_library(easypass MODULE easypass.cpp PLUGIN_TOOL opt ) target_link_libraries(easypass PRIVATE ${llvm_libs})
3. 代码可选优化
当前代码本身无错误,但如果使用LLVM 14+版本,Legacy Pass Manager已被标记为过时,建议迁移到新的Pass Manager框架;不过修复链接问题后,现有代码可正常编译运行。
内容的提问来源于stack exchange,提问作者theokipullup
相关产品推荐
相关产品推荐

