Linking a plugin against clang/LLVM libraries

Hi all,

I’m trying to compile a clang plugin against a local ToT build of clang on OSX 10.10. The link command fails like this:

$ /Users/ehsan/src/llvm-objdir/bin/clang++ -Qunused-arguments -I/Users/ehsan/src/llvm/include -I/Users/ehsan/src/llvm-objdir/include -fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wcovered-switch-default -std=c++11 -fcolor-diagnostics -O2 -g -DNDEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fno-rtti -fno-exceptions -fno-omit-frame-pointer -fPIC -shared -o libclang-plugin.dylib clang-plugin.o -L/Users/ehsan/src/llvm-objdir/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lcurses -lpthread -lz -lm -L/Users/ehsan/src/llvm-objdir/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVMOption -lLLVMBitReader -lLLVMMCParser -lLLVMAsmParser -lLLVMAnalysis -lLLVMTarget -lLLVMMC -lLLVMCore -lLLVMSupport -lclangFrontend -lclangDriver -lclangSerialization -lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST -lclangLex -lclangBasic -lclangASTMatchers -Wl,-executable_path,/Users/ehsan/moz/src/obj-ff-clang-plugin.noindex/dist/bin -dynamiclib -install_name @executable_path/libclang-plugin.dylib -compatibility_version 1 -current_version 1 -single_module
Undefined symbols for architecture x86_64:
“llvm::MemoryBuffer::getOpenFile(int, llvm::Twine const&, unsigned long long, bool, bool)”, referenced from:
(anonymous namespace)::RealFile::getBuffer(llvm::Twine const&, long long, bool, bool) in libclangBasic.a(VirtualFileSystem.cpp.o)
“llvm::MemoryBuffer::getMemBufferCopy(llvm::StringRef, llvm::Twine const&)”, referenced from:
clang::Preprocessor::EnterMainSourceFile() in libclangLex.a(Preprocessor.cpp.o)
clang::createChainedIncludesSource(clang::CompilerInstance&, llvm::IntrusiveRefCntPtrclang::ExternalSemaSource&) in libclangFrontend.a(ChainedIncludesSource.cpp.o)
clang::ASTUnit::getMainBufferWithPrecompiledPreamble(clang::CompilerInvocation const&, bool, unsigned int) in libclangFrontend.a(ASTUnit.cpp.o)
clang::GenerateModuleAction::BeginSourceFileAction(clang::CompilerInstance&, llvm::StringRef) in libclangFrontend.a(FrontendActions.cpp.o)
“llvm::MemoryBuffer::getNewUninitMemBuffer(unsigned long, llvm::Twine const&)”, referenced from:
clang::Preprocessor::SetCodeCompletionPoint(clang::FileEntry const*, unsigned int, unsigned int) in libclangLex.a(Preprocessor.cpp.o)
“llvm::MemoryBuffer::getFile(llvm::Twine const&, long long, bool, bool)”, referenced from:
clang::CompilerInvocation::getModuleHash() const in libclangFrontend.a(CompilerInvocation.cpp.o)
clang::createVFSFromCompilerInvocation(clang::CompilerInvocation const&, clang::DiagnosticsEngine&) in libclangFrontend.a(CompilerInvocation.cpp.o)
clang::GlobalModuleIndex::readIndex(llvm::StringRef) in libclangSerialization.a(GlobalModuleIndex.cpp.o)
clang::PTHManager::Create(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > const&, clang::DiagnosticsEngine&) in libclangLex.a(PTHLexer.cpp.o)
clang::driver::toolchains::Linux::Linux(clang::driver::Driver const&, llvm::Triple const&, llvm::opt::ArgList const&) in libclangDriver.a(ToolChains.cpp.o)
“llvm::sys::findProgramByName(llvm::StringRef, llvm::ArrayRefllvm::StringRef)”, referenced from:
clang::driver::Driver::GetProgramPath(char const*, clang::driver::ToolChain const&) const in libclangDriver.a(Driver.cpp.o)
“llvm::scalbn(llvm::APFloat, int)”, referenced from:
(anonymous namespace)::ComplexExprEvaluator::VisitBinaryOperator(clang::BinaryOperator const*) in libclangAST.a(ExprConstant.cpp.o)
“llvm::APInt::sshl_ov(llvm::APInt const&, bool&) const”, referenced from:
EvaluateDirectiveSubExpr((anonymous namespace)::PPValue&, unsigned int, clang::Token&, bool, clang::Preprocessor&) in libclangLex.a(PPExpressions.cpp.o)
“llvm::APInt::ushl_ov(llvm::APInt const&, bool&) const”, referenced from:
EvaluateDirectiveSubExpr((anonymous namespace)::PPValue&, unsigned int, clang::Token&, bool, clang::Preprocessor&) in libclangLex.a(PPExpressions.cpp.o)
ld: symbol(s) not found for architecture x86_64
clang-3.5: error: linker command failed with exit code 1 (use -v to see invocation)

The missing symbols are all in libLLVMSupport.a, but libLLVMSupport.dylib (which is the library that ld tries to link against) misses all of these symbols because they are not referenced by anything and were eliminated during linking.

tools/driver/CMakeLists.txt in the clang repository sets LLVM_NO_DEAD_STRIP to 1 if CLANG_PLUGIN_SUPPORT is turned on, which it is in my build, but it seems like that setting does not affect libLLVMSupport, so we end up passing -dead_strip to the linker. I’m not sure if this is the intended behavior, or a bug.

Do I understand the setup as intended correctly? Can someone please suggest a way to bypass this issue?

Thanks!

I think Nico tweaked the dead strip settings last?

tools/driver/CMakeLists.txt in the clang repository sets LLVM_NO_DEAD_STRIP
to 1 if CLANG_PLUGIN_SUPPORT is turned on, which it is in my build, but it
seems like that setting does not affect libLLVMSupport, so we end up passing
-dead_strip to the linker. I'm not sure if this is the intended behavior,
or a bug.

I am not sure about ld64's -dead_strip, but at least for the ELF
equivalent (-gc-sections) the linker is supposed to keep symbols that
are in the symbol table of a library, so passing -gc-sections is safe.

Cheers,
Rafael

I think Nico tweaked the dead strip settings last?

I tested things in a static library build. Since you say something about
libLLVMSupport.dylib, you're probably doing a shared build. From how I
remember things looking, I don't think shared builds supported plugins ever
in the make build – it's probably you'll have to tweak the build
description to get this configuration to work. (Chromium only uses compiler
plugins in static builds too.)

Nico

I think Nico tweaked the dead strip settings last?

I tested things in a static library build. Since you say something about
libLLVMSupport.dylib, you're probably doing a shared build. From how I
remember things looking, I don't think shared builds supported plugins ever
in the make build – it's probably you'll have to tweak the build
description to get this configuration to work. (Chromium only uses compiler
plugins in static builds too.)

How should I get a non-shared build? That may be the source of this
problem...

Don't says -DBUILD_SHARED_LIBS=YES when running cmake, I think. It's off by
default. What does CMakeCache.txt in your build directory store for that
setting?

hi Ehsan,

Clang plugin linking problem is a popular topic on this list. (here is a thread which discussed similar problem http://lists.cs.uiuc.edu/pipermail/cfe-dev/2013-September/031737.html ) hope it fits to your problem as well.

regards,

Laszlo

It's set to OFF. I usually build with `cmake -GNinja
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DLLVM_TARGETS_TO_BUILD=X86
/path/to/llvm`. I just did a clean rebuild and verified that I am still
getting the following dylibs in objdir/lib:

lib/BugpointPasses.dylib lib/libLTO.dylib lib/libc++.1.dylib
lib/libclang.3.7.dylib
lib/LLVMHello.dylib lib/libc++.1.0.dylib lib/libc++.dylib
lib/libclang.dylib

hi Ehsan,

Clang plugin linking problem is a popular topic on this list. (here is a
thread which discussed similar problem
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2013-September/031737.html )
hope it fits to your problem as well

Thanks for the link! This seems somewhat similar to my problem, so I
removed all -lclangFoo arguments and used -undefined dynamic_lookup
instead, and the linker did not complain, but now when I try to run clang
with my newly built plugin, the compiler returns a 1 error code with no
error messages, and doesn't seem to be doing anything.

hi Ehsan,

it’s good that you don’t have linking problem now. to fix the next problem: can you show us, how do you call the compiler with your plugin? and also try to put printouts into the code, to see where it is. or could you share the plugin source somehow?

regards,
Laszlo