How to link with LLD using llvm-config

I’m writing a toy compiler in C++ and want to invoke LLD so I can link my object file output to generate an executable. However, I can’t seem to get the LLD library linking with my project.

In my project’s makefile, I have llvm-config --cxxflags --ldflags --system-libs --libs core to get the location of all the headers and libraries I need to link with. The header files work, and I can do #include "lld/Common/Driver.h" in my project and then call lld::elf::link, and my code editor can find the function definition just fine.

However, when I try to make, it tells me that the symbol couldn’t be found, meaning it’s not linking with the actual lld object file where the function code resides.

I initially installed LLVM with homebrew, and when I run llvm-config --cxxflags --ldflags --system-libs --libs core, I get:

-I/opt/homebrew/Cellar/llvm/15.0.7_1/include -std=c++14 -stdlib=libc++   -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
-L/opt/homebrew/Cellar/llvm/15.0.7_1/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names
-lLLVM-15

Is this not enough information for clang to find the lld library? My guess is that I’m either not understanding something about how llvm-config works, or that maybe brew didn’t actually build the lld subproject so the object files just dont exist?

I’m very new to working with the LLVM project so I’m a little confused. For reference, my project uses the core LLVM library, and if I remove the LLD parts of my code, my project compiles just fine. It’s just LLD that’s acting up.

What should I do?

Are there lld libraries in that directory?

 > ls | grep -i lld
liblldCOFF.a
liblldCommon.a
liblldCore.a
liblldDriver.a
liblldELF.a
liblldMachO.a
liblldMachO2.a
liblldMinGW.a
liblldReaderWriter.a
liblldWasm.a
liblldYAML.a

There aren’t as many as you just listed:

> cd /opt/homebrew/Cellar/llvm/15.0.7_1/lib
> ls | grep -i lld
liblldCOFF.a
liblldCommon.a
liblldELF.a
liblldMachO.a
liblldMinGW.a
liblldWasm.a
liblldb.15.0.7.dylib
liblldb.dylib

Should I just build the llvm project manually instead of using brew?

You probably have to just pick the right library to link with.

I don’t really understand. Isn’t the linker searching through the whole lib directory when looking for unrecognized symbols? Shouldn’t that mean that if I use lld::elf::link in my project, that it should automatically find the symbol if it’s defined in any one of those files above?

If I understand you correctly, when you build your binary the symbol is missing. Thus, you have to add the correct library to your Makefile.

I have -L/opt/homebrew/Cellar/llvm/15.0.7_1/lib in my compiler arguments in my makefile though, so all the LLD libraries should be included there, no?

No. -L is the path. -llldElf links a file.

Ah! When I try to add that, though, I get this huge error message:

Undefined symbols for architecture arm64:
  "lld::DWARFCache::getDILineInfo(unsigned long long, unsigned long long)", referenced from:
      lld::elf::InputFile::getSrcMsg(lld::elf::Symbol const&, lld::elf::InputSectionBase&, unsigned long long) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)1, false> >::getDILineInfo(lld::elf::InputSectionBase*, unsigned long long) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)0, false> >::getDILineInfo(lld::elf::InputSectionBase*, unsigned long long) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)1, true> >::getDILineInfo(lld::elf::InputSectionBase*, unsigned long long) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)0, true> >::getDILineInfo(lld::elf::InputSectionBase*, unsigned long long) in liblldElf.a(InputFiles.cpp.o)
  "lld::DWARFCache::getVariableLoc(llvm::StringRef)", referenced from:
      lld::elf::InputFile::getSrcMsg(lld::elf::Symbol const&, lld::elf::InputSectionBase&, unsigned long long) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)1, false> >::getVariableLoc(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)0, false> >::getVariableLoc(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)1, true> >::getVariableLoc(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)0, true> >::getVariableLoc(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
  "lld::DWARFCache::DWARFCache(std::__1::unique_ptr<llvm::DWARFContext, std::__1::default_delete<llvm::DWARFContext> >)", referenced from:
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)1, false> >::getDwarf()::'lambda'()::operator()() const in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)0, false> >::getDwarf()::'lambda'()::operator()() const in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)1, true> >::getDwarf()::'lambda'()::operator()() const in liblldElf.a(InputFiles.cpp.o)
      lld::elf::ObjFile<llvm::object::ELFType<(llvm::support::endianness)0, true> >::getDwarf()::'lambda'()::operator()() const in liblldElf.a(InputFiles.cpp.o)
  "lld::checkError(llvm::Error)", referenced from:
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
      lld::elf::BitcodeCompiler::add(lld::elf::BitcodeFile&) in liblldElf.a(LTO.cpp.o)
      lld::elf::BitcodeCompiler::compile() in liblldElf.a(LTO.cpp.o)
  "lld::errorCount()", referenced from:
      lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::createFiles(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::readLinkerScript(llvm::MemoryBufferRef) in liblldElf.a(ScriptParser.cpp.o)
      lld::elf::readDefsym(llvm::StringRef, llvm::MemoryBufferRef) in liblldElf.a(ScriptParser.cpp.o)
      (anonymous namespace)::ScriptParser::readInput() in liblldElf.a(ScriptParser.cpp.o)
      ...
  "lld::saveBuffer(llvm::StringRef, llvm::Twine const&)", referenced from:
      lld::elf::BitcodeCompiler::compile() in liblldElf.a(LTO.cpp.o)
  "lld::unlinkAsync(llvm::StringRef)", referenced from:
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(Writer.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldElf.a(Writer.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)1, true> >() in liblldElf.a(Writer.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)0, true> >() in liblldElf.a(Writer.cpp.o)
  "lld::ErrorHandler::initialize(llvm::raw_ostream&, llvm::raw_ostream&, bool, bool)", referenced from:
      lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) in liblldElf.a(Driver.cpp.o)
  "lld::errorHandler()", referenced from:
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(Writer.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldElf.a(Writer.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)1, true> >() in liblldElf.a(Writer.cpp.o)
      void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)0, true> >() in liblldElf.a(Writer.cpp.o)
  "lld::commonContext()", referenced from:
      lld::elf::LinkerDriver::addLibrary(llvm::StringRef) in liblldElf.a(Driver.cpp.o)
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::createCommentSection() in liblldElf.a(SyntheticSections.cpp.o)
      lld::elf::createInterpSection() in liblldElf.a(SyntheticSections.cpp.o)
      lld::elf::readFile(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::BitcodeFile::BitcodeFile(llvm::MemoryBufferRef, llvm::StringRef, unsigned long long, bool) in liblldElf.a(InputFiles.cpp.o)
      ...
  "lld::getLLDVersion()", referenced from:
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::createCommentSection() in liblldElf.a(SyntheticSections.cpp.o)
  "lld::tryCreateFile(llvm::StringRef)", referenced from:
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
  "lld::relativeToRoot(llvm::StringRef)", referenced from:
      getArchiveMembers(llvm::MemoryBufferRef) in liblldElf.a(Driver.cpp.o)
      lld::elf::readFile(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::createResponseFile(llvm::opt::InputArgList const&) in liblldElf.a(DriverUtils.cpp.o)
  "lld::SpecificAllocBase::getOrCreate(void*, unsigned long, unsigned long, lld::SpecificAllocBase* (&)(void*))", referenced from:
      lld::elf::LinkerDriver::addFile(llvm::StringRef, bool) in liblldElf.a(Driver.cpp.o)
      lld::elf::BinaryFile* lld::make<lld::elf::BinaryFile, llvm::MemoryBufferRef&>(llvm::MemoryBufferRef&) in liblldElf.a(Driver.cpp.o)
      lld::elf::BitcodeFile* lld::make<lld::elf::BitcodeFile, llvm::MemoryBufferRef const&, llvm::StringRef&, unsigned long long const&, bool>(llvm::MemoryBufferRef const&, llvm::StringRef&, unsigned long long const&, bool&&) in liblldElf.a(Driver.cpp.o)
      lld::elf::BitcodeFile* lld::make<lld::elf::BitcodeFile, llvm::MemoryBufferRef&, char const (&) [1], int, bool&>(llvm::MemoryBufferRef&, char const (&) [1], int&&, bool&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::BssSection* lld::make<lld::elf::BssSection, char const (&) [7], unsigned long long&, unsigned int&>(char const (&) [7], unsigned long long&, unsigned int&) in liblldElf.a(Driver.cpp.o)
      lld::elf::createCommentSection() in liblldElf.a(SyntheticSections.cpp.o)
      ...
  "lld::diagnosticHandler(llvm::DiagnosticInfo const&)", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::isValidCIdentifier(llvm::StringRef)", referenced from:
      (anonymous namespace)::Writer<llvm::object::ELFType<(llvm::support::endianness)1, false> >::finalizeSections() in liblldElf.a(Writer.cpp.o)
      (anonymous namespace)::Writer<llvm::object::ELFType<(llvm::support::endianness)0, false> >::finalizeSections() in liblldElf.a(Writer.cpp.o)
      (anonymous namespace)::Writer<llvm::object::ELFType<(llvm::support::endianness)1, true> >::finalizeSections() in liblldElf.a(Writer.cpp.o)
      (anonymous namespace)::Writer<llvm::object::ELFType<(llvm::support::endianness)0, true> >::finalizeSections() in liblldElf.a(Writer.cpp.o)
      isEligible(lld::elf::InputSection*) in liblldElf.a(ICF.cpp.o)
      void lld::elf::markLive<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(MarkLive.cpp.o)
      void lld::elf::markLive<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldElf.a(MarkLive.cpp.o)
      ...
  "lld::CommonLinkerContext::CommonLinkerContext()", referenced from:
      lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) in liblldElf.a(Driver.cpp.o)
  "lld::SingleStringMatcher::SingleStringMatcher(llvm::StringRef)", referenced from:
      (anonymous namespace)::ScriptParser::readOutputSectionDescription(llvm::StringRef) in liblldElf.a(ScriptParser.cpp.o)
      (anonymous namespace)::ScriptParser::readInputSectionRules(llvm::StringRef, unsigned long long, unsigned long long) in liblldElf.a(ScriptParser.cpp.o)
      (anonymous namespace)::ScriptParser::readInputSectionsList() in liblldElf.a(ScriptParser.cpp.o)
      lld::StringMatcher::StringMatcher(llvm::StringRef) in liblldElf.a(ScriptParser.cpp.o)
      lld::elf::SymbolTable::findAllByVersion(lld::elf::SymbolVersion, bool) in liblldElf.a(SymbolTable.cpp.o)
      lld::elf::InputSectionDescription* lld::make<lld::elf::InputSectionDescription, char const (&) [1]>(char const (&) [1]) in liblldElf.a(OutputSections.cpp.o)
      lld::elf::InputSectionDescription* lld::make<lld::elf::InputSectionDescription, char const (&) [1]>(char const (&) [1]) in liblldElf.a(Relocations.cpp.o)
      ...
  "lld::getCodeModelFromCMModel()", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::getRelocModelFromCMModel()", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::initTargetOptionsFromCodeGenFlags()", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::log(llvm::Twine const&)", referenced from:
      lld::elf::RelrSection<llvm::object::ELFType<(llvm::support::endianness)1, false> >::updateAllocSize() in liblldElf.a(SyntheticSections.cpp.o)
      lld::elf::RelrSection<llvm::object::ELFType<(llvm::support::endianness)0, false> >::updateAllocSize() in liblldElf.a(SyntheticSections.cpp.o)
      lld::elf::RelrSection<llvm::object::ELFType<(llvm::support::endianness)1, true> >::updateAllocSize() in liblldElf.a(SyntheticSections.cpp.o)
      lld::elf::RelrSection<llvm::object::ELFType<(llvm::support::endianness)0, true> >::updateAllocSize() in liblldElf.a(SyntheticSections.cpp.o)
      lld::elf::readFile(llvm::StringRef) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::LinkerScript::processSectionCommands() in liblldElf.a(LinkerScript.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(ICF.cpp.o)
      ...
  "lld::args::getInteger(llvm::opt::InputArgList&, unsigned int, long long)", referenced from:
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
  "lld::args::getStrings(llvm::opt::InputArgList&, int)", referenced from:
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
  "lld::args::getCGOptLevel(int)", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::args::getZOptionValue(llvm::opt::InputArgList&, int, llvm::StringRef, unsigned long long)", referenced from:
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
  "lld::args::getFilenameWithoutExe(llvm::StringRef)", referenced from:
      lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) in liblldElf.a(Driver.cpp.o)
  "lld::args::getLines(llvm::MemoryBufferRef)", referenced from:
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
  "lld::errs()", referenced from:
      lld::elf::ELFOptTable::parse(llvm::ArrayRef<char const*>) in liblldElf.a(DriverUtils.cpp.o)
  "lld::outs()", referenced from:
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::parseFile(lld::elf::InputFile*) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::printHelp() in liblldElf.a(DriverUtils.cpp.o)
      lld::elf::printTraceSymbol(lld::elf::Symbol const&, llvm::StringRef) in liblldElf.a(Symbols.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(ICF.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldElf.a(ICF.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)1, true> >() in liblldElf.a(ICF.cpp.o)
      ...
  "lld::warn(llvm::Twine const&)", referenced from:
      lld::elf::errorOrWarn(llvm::Twine const&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::addFile(llvm::StringRef, bool) in liblldElf.a(Driver.cpp.o)
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      readAddressAreas(llvm::DWARFContext&, lld::elf::InputSection*) in liblldElf.a(SyntheticSections.cpp.o)
      void llvm::function_ref<void (llvm::Error)>::callback_fn<llvm::SmallVector<lld::elf::GdbIndexSection::NameAttrEntry, 0u> readPubNamesAndTypes<llvm::object::ELFType<(llvm::support::endianness)1, false> >(lld::elf::LLDDwarfObj<llvm::object::ELFType<(llvm::support::endianness)1, false> > const&, llvm::SmallVectorImpl<lld::elf::GdbIndexSection::CuEntry> const&)::'lambda'(llvm::Error)>(long, llvm::Error) in liblldElf.a(SyntheticSections.cpp.o)
      ...
  "lld::error(llvm::Twine const&)", referenced from:
      lld::elf::errorOrWarn(llvm::Twine const&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::addFile(llvm::StringRef, bool) in liblldElf.a(Driver.cpp.o)
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::createFiles(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::inferMachineType() in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      ...
  "lld::error(llvm::Twine const&, lld::ErrorTag, llvm::ArrayRef<llvm::StringRef>)", referenced from:
      lld::elf::LinkerDriver::addLibrary(llvm::StringRef) in liblldElf.a(Driver.cpp.o)
      reportUndefinedSymbol((anonymous namespace)::UndefinedDiag const&, bool) in liblldElf.a(Relocations.cpp.o)
  "lld::fatal(llvm::Twine const&)", referenced from:
      getArchiveMembers(llvm::MemoryBufferRef) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
      std::__1::unique_ptr<llvm::object::Archive, std::__1::default_delete<llvm::object::Archive> > lld::check2<std::__1::unique_ptr<llvm::object::Archive, std::__1::default_delete<llvm::object::Archive> > >(llvm::Expected<std::__1::unique_ptr<llvm::object::Archive, std::__1::default_delete<llvm::object::Archive> > >, llvm::function_ref<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>) in liblldElf.a(Driver.cpp.o)
      llvm::MemoryBufferRef lld::check2<llvm::MemoryBufferRef>(llvm::Expected<llvm::MemoryBufferRef>, llvm::function_ref<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>) in liblldElf.a(Driver.cpp.o)
      std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > lld::check<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(llvm::Expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >) in liblldElf.a(Driver.cpp.o)
      llvm::CachePruningPolicy lld::check2<llvm::CachePruningPolicy>(llvm::Expected<llvm::CachePruningPolicy>, llvm::function_ref<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>) in liblldElf.a(Driver.cpp.o)
      lld::elf::LinkerDriver::link(llvm::opt::InputArgList&)::$_5::operator()(lld::elf::InputSectionBase*) const in liblldElf.a(Driver.cpp.o)
      ...
  "lld::quote(llvm::StringRef)", referenced from:
      lld::elf::createResponseFile(llvm::opt::InputArgList const&) in liblldElf.a(DriverUtils.cpp.o)
  "lld::message(llvm::Twine const&, llvm::raw_ostream&)", referenced from:
      lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) in liblldElf.a(Driver.cpp.o)
      lld::elf::parseFile(lld::elf::InputFile*) in liblldElf.a(InputFiles.cpp.o)
      lld::elf::printTraceSymbol(lld::elf::Symbol const&, llvm::StringRef) in liblldElf.a(Symbols.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(ICF.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldElf.a(ICF.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)1, true> >() in liblldElf.a(ICF.cpp.o)
      void lld::elf::doIcf<llvm::object::ELFType<(llvm::support::endianness)0, true> >() in liblldElf.a(ICF.cpp.o)
      ...
  "lld::parseHex(llvm::StringRef)", referenced from:
      readConfigs(llvm::opt::InputArgList&) in liblldElf.a(Driver.cpp.o)
  "lld::toString(llvm::opt::Arg const&)", referenced from:
      lld::elf::createResponseFile(llvm::opt::InputArgList const&) in liblldElf.a(DriverUtils.cpp.o)
  "lld::getCPUStr()", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::getMAttrs()", referenced from:
      lld::elf::BitcodeCompiler::BitcodeCompiler() in liblldElf.a(LTO.cpp.o)
  "lld::StringMatcher::match(llvm::StringRef) const", referenced from:
      lld::elf::SectionPattern::excludesFile(lld::elf::InputFile const*) const in liblldElf.a(LinkerScript.cpp.o)
      lld::elf::LinkerScript::shouldKeep(lld::elf::InputSectionBase*) in liblldElf.a(LinkerScript.cpp.o)
      lld::elf::LinkerScript::computeInputSections(lld::elf::InputSectionDescription const*, llvm::ArrayRef<lld::elf::InputSectionBase*>) in liblldElf.a(LinkerScript.cpp.o)
  "lld::SingleStringMatcher::match(llvm::StringRef) const", referenced from:
      lld::elf::SymbolTable::findAllByVersion(lld::elf::SymbolVersion, bool) in liblldElf.a(SymbolTable.cpp.o)
      lld::elf::InputSectionDescription::matchesFile(lld::elf::InputFile const*) const in liblldElf.a(LinkerScript.cpp.o)
      lld::elf::LinkerScript::shouldKeep(lld::elf::InputSectionBase*) in liblldElf.a(LinkerScript.cpp.o)
      lld::elf::LinkerScript::computeInputSections(lld::elf::InputSectionDescription const*, llvm::ArrayRef<lld::elf::InputSectionBase*>) in liblldElf.a(LinkerScript.cpp.o)
  "_adler32", referenced from:
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, false> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldElf.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, false> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldElf.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, true> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldElf.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, true> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldElf.a(OutputSections.cpp.o)
  "_adler32_combine", referenced from:
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldElf.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldElf.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, true> >() in liblldElf.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, true> >() in liblldElf.a(OutputSections.cpp.o)
  "_deflate", referenced from:
      deflateShard(llvm::ArrayRef<unsigned char>, int, int) in liblldElf.a(OutputSections.cpp.o)
  "_deflateEnd", referenced from:
      deflateShard(llvm::ArrayRef<unsigned char>, int, int) in liblldElf.a(OutputSections.cpp.o)
  "_deflateInit2_", referenced from:
      deflateShard(llvm::ArrayRef<unsigned char>, int, int) in liblldElf.a(OutputSections.cpp.o)
ld: symbol(s) not found for architecture arm64
clang-15: error: linker command failed with exit code 1 (use -v to see invocation)

I got it down a little by including -llldCommon, but there are still a few symbols that I can’t seem to find:

Undefined symbols for architecture arm64:
  "_adler32", referenced from:
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, false> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldELF.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, false> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldELF.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, true> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldELF.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, true> >()::'lambda'(unsigned long)::operator()(unsigned long) const in liblldELF.a(OutputSections.cpp.o)
  "_adler32_combine", referenced from:
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, false> >() in liblldELF.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, false> >() in liblldELF.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)1, true> >() in liblldELF.a(OutputSections.cpp.o)
      void lld::elf::OutputSection::maybeCompress<llvm::object::ELFType<(llvm::support::endianness)0, true> >() in liblldELF.a(OutputSections.cpp.o)
  "_deflate", referenced from:
      deflateShard(llvm::ArrayRef<unsigned char>, int, int) in liblldELF.a(OutputSections.cpp.o)
  "_deflateEnd", referenced from:
      deflateShard(llvm::ArrayRef<unsigned char>, int, int) in liblldELF.a(OutputSections.cpp.o)
  "_deflateInit2_", referenced from:
      deflateShard(llvm::ArrayRef<unsigned char>, int, int) in liblldELF.a(OutputSections.cpp.o)
ld: symbol(s) not found for architecture arm64

Oh, I just figured it out! Had to link zlib as well with -lz.
Okay, that’s fixed. Thank you for all the help! I do have a follow up question now though…

I’m not sure what arguments lld::elf::link is even supposed to take. I’m looking at the very limited reference on LLD right now and I can’t find any helpful documentation. The function header looks like:

bool lld::elf::link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput)

First of all I have no clue what exitEarly or disableOutput do. I also assume I can just pass in llvm::outs() and llvm::errs() as the stdoutOS and stderrOS arguments?

I’m also not sure how I would convert the normal args I’d give the linker if I were invoking it from the command line to the args array it needs here. I tried something like:

lld::elf::link({"-arch",
                    "arm64",
                    "-platform_version",
                    "macos",
                    "13.0.0",
                    "13.0.0",
                    "out.o"},
                   llvm::outs(), llvm::errs(), false, false);

But it did not like that:

-arch: error: unknown argument '-platform_version'
-arch: error: cannot open arm64: No such file or directory
-arch: error: cannot open macos: No such file or directory
-arch: error: cannot open 13.0.0: No such file or directory
-arch: error: cannot open 13.0.0: No such file or directory
-arch: error: out.o: unknown file type

Sorry for the constant replies. I’m very new to LLVM and would appreciate any help or just a point in the right direction!

The recommended way to invoke the linker is to invoke cc. It is knows all the flags and paths and will then invoke the linker for you. Only few people use LLD as library.

Ah, that’s disappointing. That’s actually what I had before, just doing system("cc out.o"). I don’t really like using system which is why I was trying to do this in the first place. Is there maybe a better way to invoke cc?