[lld] lld: Add initial support for GNU LTO format (PR #157175)
Tulio Magno Quites Machado Filho via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 10 14:49:13 PDT 2025
https://github.com/tuliom updated https://github.com/llvm/llvm-project/pull/157175
>From f74631668f91a791596bac345d5273ff95e651ce Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 17:54:24 -0300
Subject: [PATCH 01/14] lld: Add support for -plugin
Modify current tests in order to support -plugin.
---
lld/ELF/Config.h | 1 +
lld/ELF/Driver.cpp | 8 ++++++++
lld/ELF/Options.td | 2 +-
lld/test/ELF/Inputs/plugin.so | 0
lld/test/ELF/ignore-plugin.test | 2 --
lld/test/ELF/lto-plugin-ignore.s | 1 -
lld/test/ELF/plugin.test | 21 +++++++++++++++++++++
7 files changed, 31 insertions(+), 4 deletions(-)
create mode 100644 lld/test/ELF/Inputs/plugin.so
delete mode 100644 lld/test/ELF/ignore-plugin.test
create mode 100644 lld/test/ELF/plugin.test
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index a83a4c1176f6f..a71bf47959767 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -241,6 +241,7 @@ struct Config {
llvm::StringRef optRemarksPasses;
llvm::StringRef optRemarksFormat;
llvm::StringRef optStatsFilename;
+ llvm::StringRef plugin;
llvm::StringRef progName;
llvm::StringRef printArchiveStats;
llvm::StringRef printSymbolOrder;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 6c2f318ffe469..869a549a5482a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1806,6 +1806,14 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
<< arg->getValue() << "'";
}
+ // Ignore -plugin=LLVMgold.so because we don't need to load it.
+ StringRef v = args.getLastArgValue(OPT_plugin);
+ if (!v.empty() && !v.ends_with("LLVMgold.so"))
+ if (!llvm::sys::fs::exists(v))
+ ErrAlways(ctx) << "Cannot find plugin " << v;
+ else
+ ctx.arg.plugin = v;
+
ctx.arg.passPlugins = args::getStrings(args, OPT_load_pass_plugins);
// Parse -mllvm options.
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index cc91680550b4b..e3a5cabb74a42 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -793,7 +793,7 @@ def: J<"plugin-opt=thinlto-prefix-replace=">,
// just ignore the option on lld side as it's easier. In fact, the linker could
// be called 'ld' and understanding which linker is used would require parsing of
// --version output.
-defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">;
+defm plugin: Eq<"plugin", "Use a plugin in the linking process">;
def plugin_opt_eq_minus: J<"plugin-opt=-">,
HelpText<"Specify an LLVM option for compatibility with LLVMgold.so">;
diff --git a/lld/test/ELF/Inputs/plugin.so b/lld/test/ELF/Inputs/plugin.so
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/lld/test/ELF/ignore-plugin.test b/lld/test/ELF/ignore-plugin.test
deleted file mode 100644
index fcd3fa64195c4..0000000000000
--- a/lld/test/ELF/ignore-plugin.test
+++ /dev/null
@@ -1,2 +0,0 @@
-RUN: not ld.lld --plugin foo 2>&1 | FileCheck %s
-CHECK: no input files
diff --git a/lld/test/ELF/lto-plugin-ignore.s b/lld/test/ELF/lto-plugin-ignore.s
index dd39139b62439..a105e61f88823 100644
--- a/lld/test/ELF/lto-plugin-ignore.s
+++ b/lld/test/ELF/lto-plugin-ignore.s
@@ -6,7 +6,6 @@
## GCC collect2 passes several LTO related options to the linker even if -flto is not used.
## We need to ignore them. Note that the lto-wrapper path can be relative.
# RUN: ld.lld %t.o -o /dev/null \
-# RUN: -plugin path/to/liblto_plugin.so \
# RUN: -plugin-opt=/path/to/lto-wrapper \
# RUN: -plugin-opt=/path/to/lto-wrapper.exe \
# RUN: -plugin-opt=relative/path/to/lto-wrapper \
diff --git a/lld/test/ELF/plugin.test b/lld/test/ELF/plugin.test
new file mode 100644
index 0000000000000..e72ffd49339a6
--- /dev/null
+++ b/lld/test/ELF/plugin.test
@@ -0,0 +1,21 @@
+RUN: not ld.lld -plugin %S/Inputs/plugin.so 2>&1 | FileCheck %s
+RUN: not ld.lld -plugin=%S/Inputs/plugin.so 2>&1 | FileCheck %s
+RUN: not ld.lld --plugin %S/Inputs/plugin.so 2>&1 | FileCheck %s
+RUN: not ld.lld --plugin=%S/Inputs/plugin.so 2>&1 | FileCheck %s
+
+RUN: cd %S
+RUN: not ld.lld -plugin Inputs/plugin.so 2>&1 | FileCheck %s
+RUN: not ld.lld -plugin=Inputs/plugin.so 2>&1 | FileCheck %s
+RUN: not ld.lld --plugin Inputs/plugin.so 2>&1 | FileCheck %s
+RUN: not ld.lld --plugin=Inputs/plugin.so 2>&1 | FileCheck %s
+
+CHECK: no input files
+CHECK-NOT: unknown argument
+
+RUN: not ld.lld -plugin foo 2>&1 | FileCheck --check-prefix=MISSING %s
+RUN: not ld.lld -plugin=foo 2>&1 | FileCheck --check-prefix=MISSING %s
+RUN: not ld.lld --plugin foo 2>&1 | FileCheck --check-prefix=MISSING %s
+RUN: not ld.lld --plugin=foo 2>&1 | FileCheck --check-prefix=MISSING %s
+
+MISSING: Cannot find plugin foo
+MISSING-NOT: unknown argument
>From fd5a4912f0f70d013af530b8cd4b090eb0daad9a Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 17:54:24 -0300
Subject: [PATCH 02/14] lld: Split the LTO code in order to add support for GCC
LTO
Rename classes BitcodeFile and BitcodeCompiler to IRFile and IRCompiler
respectively. This helps to reuse their code in the new GCC-related
classes while keeping their names semantically correct.
Part of the code from IRCompiler that is specific to LLVM got moved to a
new class called BitcodeCompiler.
The new class GccIRCompiler abstracts the interface to GCC's
liblto_plugin.so.
---
lld/ELF/CMakeLists.txt | 5 ++
lld/ELF/Config.h | 4 +-
lld/ELF/Driver.cpp | 17 +++++-
lld/ELF/InputFiles.cpp | 6 +-
lld/ELF/InputFiles.h | 21 +++++--
lld/ELF/LTO.cpp | 91 ++++++++++++++++++++++++++++--
lld/ELF/LTO.h | 56 ++++++++++++++++--
lld/cmake/modules/FindGNULTO.cmake | 38 +++++++++++++
8 files changed, 216 insertions(+), 22 deletions(-)
create mode 100644 lld/cmake/modules/FindGNULTO.cmake
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index ec3f6382282b1..b0e051a059951 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -18,6 +18,11 @@ if(LLVM_ENABLE_ZSTD)
list(APPEND imported_libs ${zstd_target})
endif()
+set(GNULTO_INCLUDE_DIR "" CACHE PATH "Additional directory, where CMake should search for plugin-api.h")
+if(LLD_ENABLE_GNULTO)
+ find_package(GNULTO REQUIRED)
+endif()
+
add_lld_library(lldELF
AArch64ErrataFix.cpp
Arch/AArch64.cpp
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index a71bf47959767..f7a439ca75118 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -54,6 +54,7 @@ class TargetInfo;
struct Ctx;
struct Partition;
struct PhdrEntry;
+class IRCompiler;
class BssSection;
class GdbIndexSection;
@@ -191,6 +192,7 @@ class LinkerDriver {
void inferMachineType();
template <class ELFT> void link(llvm::opt::InputArgList &args);
template <class ELFT> void compileBitcodeFiles(bool skipLinkedOutput);
+ template <class ELFT> void compileGccIRFiles(bool skipLinkedOutput);
bool tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive, bool lazy);
// True if we are in --whole-archive and --no-whole-archive.
@@ -199,7 +201,7 @@ class LinkerDriver {
// True if we are in --start-lib and --end-lib.
bool inLib = false;
- std::unique_ptr<BitcodeCompiler> lto;
+ std::unique_ptr<IRCompiler> lto;
SmallVector<std::unique_ptr<InputFile>, 0> files, ltoObjectFiles;
public:
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 869a549a5482a..aecc15eb89ea5 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2691,7 +2691,7 @@ static void markBuffersAsDontNeed(Ctx &ctx, bool skipLinkedOutput) {
}
// This function is where all the optimizations of link-time
-// optimization takes place. When LTO is in use, some input files are
+// optimization takes place. When LLVM LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
// This function compiles bitcode files into a few big native files
// using LLVM functions and replaces bitcode symbols with the results.
@@ -2740,6 +2740,15 @@ void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
}
}
+template <class ELFT>
+void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
+ llvm::TimeTraceScope timeScope("LTO");
+ // Compile files and replace symbols.
+ lto.reset(GccIRCompiler::getInstance(ctx));
+
+ return;
+}
+
// The --wrap option is a feature to rename symbols so that you can write
// wrappers for existing functions. If you pass `--wrap=foo`, all
// occurrences of symbol `foo` are resolved to `__wrap_foo` (so, you are
@@ -3297,7 +3306,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// except a few linker-synthesized ones will be added to the symbol table.
const size_t numObjsBeforeLTO = ctx.objectFiles.size();
const size_t numInputFilesBeforeLTO = ctx.driver.files.size();
- compileBitcodeFiles<ELFT>(skipLinkedOutput);
+ if (ctx.arg.plugin.empty()) {
+ compileBitcodeFiles<ELFT>(skipLinkedOutput);
+ } else {
+ compileGccIRFiles<ELFT>(skipLinkedOutput);
+ }
// Symbol resolution finished. Report backward reference problems,
// --print-archive-stats=, and --why-extract=.
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index a5921feb18299..8b9b55c4a5d47 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -1845,8 +1845,8 @@ static bool dtltoAdjustMemberPathIfThinArchive(Ctx &ctx, StringRef archivePath,
return true;
}
-BitcodeFile::BitcodeFile(Ctx &ctx, MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive, bool lazy)
+IRFile::IRFile(Ctx &ctx, MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive, bool lazy)
: InputFile(ctx, BitcodeKind, mb) {
this->archiveName = archiveName;
this->lazy = lazy;
@@ -1958,7 +1958,7 @@ void BitcodeFile::parse() {
addDependentLibrary(ctx, l, this);
}
-void BitcodeFile::parseLazy() {
+void IRFile::parseLazy() {
numSymbols = obj->symbols().size();
symbols = std::make_unique<Symbol *[]>(numSymbols);
for (auto [i, irSym] : llvm::enumerate(obj->symbols())) {
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index ba844ad18f637..551fbb85cb84f 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -16,6 +16,7 @@
#include "lld/Common/Reproduce.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Threading.h"
@@ -321,15 +322,25 @@ template <class ELFT> class ObjFile : public ELFFileBase {
ArrayRef<Elf_Word> shndxTable;
};
-class BitcodeFile : public InputFile {
+class IRFile : public InputFile {
public:
- BitcodeFile(Ctx &, MemoryBufferRef m, StringRef archiveName,
- uint64_t offsetInArchive, bool lazy);
+ IRFile(Ctx &ctx, MemoryBufferRef m, StringRef archiveName, uint64_t offsetInArchive,
+ bool lazy);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
- void parse();
+ virtual void parse() = 0;
void parseLazy();
- void postParse();
+ virtual void postParse() = 0;
std::unique_ptr<llvm::lto::InputFile> obj;
+};
+
+class BitcodeFile : public IRFile {
+public:
+ BitcodeFile(Ctx &ctx, MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive, bool lazy)
+ : IRFile(ctx, m, archiveName, offsetInArchive, lazy) {};
+ static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
+ void parse() override;
+ void postParse() override;
std::vector<bool> keptComdats;
};
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 8d4a6c9e3a81e..63458d93ce9b6 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -26,6 +26,8 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include <cstddef>
+#include <cstring>
+#include <dlfcn.h>
#include <memory>
#include <string>
#include <system_error>
@@ -165,7 +167,7 @@ static lto::Config createConfig(Ctx &ctx) {
return c;
}
-BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
+BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : IRCompiler(ctx) {
// Initialize indexFile.
if (!ctx.arg.thinLTOIndexOnlyArg.empty())
indexFile = openFile(ctx.arg.thinLTOIndexOnlyArg);
@@ -215,9 +217,7 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
}
}
-BitcodeCompiler::~BitcodeCompiler() = default;
-
-void BitcodeCompiler::add(BitcodeFile &f) {
+void IRCompiler::add(IRFile &f) {
lto::InputFile &obj = *f.obj;
bool isExec = !ctx.arg.shared && !ctx.arg.relocatable;
@@ -278,7 +278,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
// their values are still not final.
r.LinkerRedefined = sym->scriptDefined;
}
- checkError(ctx.e, ltoObj->add(std::move(f.obj), resols));
+ addObject(f, resols);
}
// If LazyObjFile has not been added to link, emit empty index files.
@@ -421,3 +421,84 @@ SmallVector<std::unique_ptr<InputFile>, 0> BitcodeCompiler::compile() {
}
return ret;
}
+
+void BitcodeCompiler::addObject(IRFile &f,
+ std::vector<llvm::lto::SymbolResolution> &r) {
+ checkError(ctx.e, ltoObj->add(std::move(f.obj), r));
+}
+
+GccIRCompiler *GccIRCompiler::singleton = nullptr;
+
+ GccIRCompiler *GccIRCompiler::getInstance() {
+ assert(singleton != nullptr);
+ return singleton;
+}
+
+GccIRCompiler *GccIRCompiler::getInstance(Ctx &ctx) {
+ if (singleton == nullptr) {
+ singleton = new GccIRCompiler(ctx);
+ }
+
+ return singleton;
+}
+
+GccIRCompiler::GccIRCompiler(Ctx &ctx) : IRCompiler(ctx) {
+ singleton = nullptr;
+
+ // TODO: Properly find the right size.
+ int tvsz = 100;
+ tv = new ld_plugin_tv[tvsz];
+ initializeTv();
+ plugin = dlopen(ctx.arg.plugin.data(), RTLD_NOW);
+ if (plugin == NULL) {
+ error(dlerror());
+ return;
+ }
+ void *tmp = dlsym(plugin, "onload");
+ if (tmp == NULL) {
+ error("Plugin does not provide onload()");
+ return;
+ }
+
+ ld_plugin_onload onload;
+ // Ensure source and destination types have the same size.
+ assert(sizeof(ld_plugin_onload) == sizeof(void *));
+ std::memcpy(&onload, &tmp, sizeof(ld_plugin_onload));
+
+ (*onload)(tv);
+
+}
+
+GccIRCompiler::~GccIRCompiler() {
+ delete tv;
+ singleton = nullptr;
+}
+
+void GccIRCompiler::initializeTv() {
+ int i = 0;
+
+#define TVU_SETTAG(t, f, v) \
+ { \
+ tv[i].tv_tag = t; \
+ tv[i].tv_u.tv_##f = v; \
+ i++; \
+ }
+
+ TVU_SETTAG(LDPT_MESSAGE, message, message);
+ TVU_SETTAG(LDPT_API_VERSION, val, LD_PLUGIN_API_VERSION);
+}
+
+SmallVector<std::unique_ptr<InputFile>, 0> GccIRCompiler::compile() {
+ SmallVector<std::unique_ptr<InputFile>, 0> ret;
+ // TODO: Implement this function.
+ return ret;
+}
+
+void GccIRCompiler::addObject(IRFile &f,
+ std::vector<llvm::lto::SymbolResolution> &r) {}
+
+enum ld_plugin_status GccIRCompiler::message(int level, const char *format,
+ ...) {
+ // TODO: Implement this function.
+ return LDPS_OK;
+}
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index acf3bcff7f2f1..63b3ca7a36be5 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -25,36 +25,80 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
+#include <plugin-api.h>
#include <vector>
namespace llvm::lto {
class LTO;
+class SymbolResolution;
}
namespace lld::elf {
struct Ctx;
class BitcodeFile;
+class ELFFileBase;
class InputFile;
+class IRFile;
+class BinaryFile;
+
+class IRCompiler {
+protected:
+ Ctx &ctx;
+ llvm::DenseSet<StringRef> thinIndices;
+ llvm::DenseSet<StringRef> usedStartStop;
+ virtual void addObject(IRFile &f,
+ std::vector<llvm::lto::SymbolResolution> &r) = 0;
+
+public:
+ IRCompiler(Ctx &ctx) : ctx(ctx) {}
+ void add(IRFile &f);
+ virtual SmallVector<std::unique_ptr<InputFile>, 0> compile() = 0;
+};
+
+class BitcodeCompiler : public IRCompiler {
+protected:
+ void addObject(IRFile &f,
+ std::vector<llvm::lto::SymbolResolution> &r) override;
-class BitcodeCompiler {
public:
BitcodeCompiler(Ctx &ctx);
~BitcodeCompiler();
- void add(BitcodeFile &f);
- SmallVector<std::unique_ptr<InputFile>, 0> compile();
+ void add(BinaryFile &f);
+ SmallVector<std::unique_ptr<InputFile>, 0> compile() override;
private:
- Ctx &ctx;
std::unique_ptr<llvm::lto::LTO> ltoObj;
// An array of (module name, native relocatable file content) pairs.
SmallVector<std::pair<std::string, SmallString<0>>, 0> buf;
std::vector<std::unique_ptr<MemoryBuffer>> files;
SmallVector<std::string, 0> filenames;
- llvm::DenseSet<StringRef> usedStartStop;
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
- llvm::DenseSet<StringRef> thinIndices;
};
+
+class GccIRCompiler : public IRCompiler {
+protected:
+ void addObject(IRFile &f,
+ std::vector<llvm::lto::SymbolResolution> &r) override;
+
+public:
+ ~GccIRCompiler();
+ static GccIRCompiler *getInstance();
+ static GccIRCompiler *getInstance(Ctx &ctx);
+
+ SmallVector<std::unique_ptr<InputFile>, 0> compile() override;
+ static enum ld_plugin_status message(int level, const char *format, ...);
+
+private:
+ GccIRCompiler(Ctx &ctx);
+ static GccIRCompiler *singleton;
+ struct ld_plugin_tv *tv;
+ // Handle for the shared library created via dlopen().
+ void *plugin;
+
+ void initializeTv();
+};
+
} // namespace lld::elf
#endif
diff --git a/lld/cmake/modules/FindGNULTO.cmake b/lld/cmake/modules/FindGNULTO.cmake
new file mode 100644
index 0000000000000..2ba603bf489e2
--- /dev/null
+++ b/lld/cmake/modules/FindGNULTO.cmake
@@ -0,0 +1,38 @@
+# Attempts to discover GCC plugin API.
+#
+# Example usage:
+#
+# find_package(GNULTO)
+#
+# If successful, the following variables will be defined:
+# HAVE_GNULTO_H
+#
+# HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2_SRC is defined depending on the
+# features available in plugin-api.h.
+
+find_path(GNULTO_INCLUDE_DIRS plugin-api.h PATHS ${GNULTO_INCLUDE_DIR})
+if( EXISTS "${GNULTO_INCLUDE_DIRS}/plugin-api.h" )
+ set(HAVE_GNULTO_H 1 CACHE INTERNAL "")
+
+ include(CMakePushCheckState)
+ cmake_push_check_state()
+ set(HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2_SRC [=[
+ #include <stdint.h>
+ #include <stddef.h>
+ #include <plugin-api.h>
+ void test(struct ld_plugin_tv *tv) {
+ tv->tv_register_claim_file_v2 = NULL;
+ }
+ ]=])
+ if(DEFINED CMAKE_C_COMPILER)
+ include(CheckCSourceCompiles)
+ check_c_source_compiles("${HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2_SRC}" HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2)
+ else()
+ include(CheckCXXSourceCompiles)
+ check_cxx_source_compiles("${HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2_SRC}" HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2)
+ endif()
+ cmake_pop_check_state()
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GNULTO DEFAULT_MSG)
>From 198eb0b6d6e844d3e960c9a67a9973649c50d659 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 17:54:25 -0300
Subject: [PATCH 03/14] lld: Add initial code for liblto_plugin
Implement the most basic functions from GccIRCompiler that let LLD link
simple executables.
Add a config.h file in order to detect at configure time if
liblto_plugin supports version 2 of LDPT_REGISTER_CLAIM_FILE_HOOK.
---
lld/ELF/CMakeLists.txt | 4 +
lld/ELF/Config.h | 2 +
lld/ELF/Driver.cpp | 55 ++++++++---
lld/ELF/LTO.cpp | 163 ++++++++++++++++++++++++++++++++-
lld/ELF/LTO.h | 17 ++++
lld/ELF/Options.td | 6 +-
lld/include/lld/config.h.cmake | 10 ++
7 files changed, 238 insertions(+), 19 deletions(-)
create mode 100644 lld/include/lld/config.h.cmake
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index b0e051a059951..66e898350aad5 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -23,6 +23,10 @@ if(LLD_ENABLE_GNULTO)
find_package(GNULTO REQUIRED)
endif()
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../include/lld/config.h.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/../include/lld/config.h)
+
add_lld_library(lldELF
AArch64ErrataFix.cpp
Arch/AArch64.cpp
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index f7a439ca75118..bb9638387a15f 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -244,9 +244,11 @@ struct Config {
llvm::StringRef optRemarksFormat;
llvm::StringRef optStatsFilename;
llvm::StringRef plugin;
+ llvm::SmallVector<std::string, 0> pluginOpt;
llvm::StringRef progName;
llvm::StringRef printArchiveStats;
llvm::StringRef printSymbolOrder;
+ llvm::StringRef resolutionFile;
llvm::StringRef soName;
llvm::StringRef sysroot;
llvm::StringRef thinLTOCacheDir;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index aecc15eb89ea5..ca365671da7c1 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1786,6 +1786,14 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
cl::ResetAllOptionOccurrences();
+ // Ignore -plugin=LLVMgold.so because we don't need to load it.
+ StringRef v = args.getLastArgValue(OPT_plugin);
+ if (!v.empty() && !v.ends_with("LLVMgold.so"))
+ if (!llvm::sys::fs::exists(v))
+ ErrAlways(ctx) << "Cannot find plugin " << v;
+ else
+ ctx.arg.plugin = v;
+
// Parse LTO options.
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
parseClangOption(ctx, ctx.saver.save("-mcpu=" + StringRef(arg->getValue())),
@@ -1796,23 +1804,26 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
arg->getSpelling());
// GCC collect2 passes -plugin-opt=path/to/lto-wrapper with an absolute or
- // relative path. Just ignore. If not ended with "lto-wrapper" (or
- // "lto-wrapper.exe" for GCC cross-compiled for Windows), consider it an
- // unsupported LLVMgold.so option and error.
+ // relative path. If not ended with "lto-wrapper" (or "lto-wrapper.exe" for
+ // GCC cross-compiled for Windows), consider it an unsupported LLVMgold.so
+ // option and error.
for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq)) {
StringRef v(arg->getValue());
if (!v.ends_with("lto-wrapper") && !v.ends_with("lto-wrapper.exe"))
ErrAlways(ctx) << arg->getSpelling() << ": unknown plugin option '"
<< arg->getValue() << "'";
+ else if (!ctx.arg.plugin.empty())
+ ctx.arg.pluginOpt.push_back(v.str());
}
- // Ignore -plugin=LLVMgold.so because we don't need to load it.
- StringRef v = args.getLastArgValue(OPT_plugin);
- if (!v.empty() && !v.ends_with("LLVMgold.so"))
- if (!llvm::sys::fs::exists(v))
- ErrAlways(ctx) << "Cannot find plugin " << v;
- else
- ctx.arg.plugin = v;
+ // Parse GCC collect2 options.
+ if (!ctx.arg.plugin.empty()) {
+ StringRef v = args.getLastArgValue(OPT_plugin_opt_fresolution);
+ if (!v.empty()) {
+ ctx.arg.resolutionFile = v;
+ ctx.arg.pluginOpt.push_back(std::string("-fresolution=" + v.str()));
+ }
+ }
ctx.arg.passPlugins = args::getStrings(args, OPT_load_pass_plugins);
@@ -2744,8 +2755,30 @@ template <class ELFT>
void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
llvm::TimeTraceScope timeScope("LTO");
// Compile files and replace symbols.
- lto.reset(GccIRCompiler::getInstance(ctx));
+ GccIRCompiler *c = GccIRCompiler::getInstance(ctx);
+ lto.reset(c);
+
+ for (ELFFileBase *file : ctx.objectFiles)
+ c->add(*file);
+
+ ltoObjectFiles = c->compile();
+ for (auto &file : ltoObjectFiles) {
+ auto *obj = cast<ObjFile<ELFT>>(file.get());
+ obj->parse(/*ignoreComdats=*/true);
+ // For defined symbols in non-relocatable output,
+ // compute isExported and parse '@'.
+ if (!ctx.arg.relocatable)
+ for (Symbol *sym : obj->getGlobalSymbols()) {
+ if (!sym->isDefined())
+ continue;
+ if (ctx.arg.exportDynamic && sym->computeBinding(ctx) != STB_LOCAL)
+ sym->isExported = true;
+ if (sym->hasVersionSuffix)
+ sym->parseSymbolVersion(ctx);
+ }
+ ctx.objectFiles.push_back(obj);
+ }
return;
}
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 63458d93ce9b6..df05e5521a37a 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -437,6 +437,7 @@ GccIRCompiler *GccIRCompiler::singleton = nullptr;
GccIRCompiler *GccIRCompiler::getInstance(Ctx &ctx) {
if (singleton == nullptr) {
singleton = new GccIRCompiler(ctx);
+ singleton->loadPlugin();
}
return singleton;
@@ -449,6 +450,14 @@ GccIRCompiler::GccIRCompiler(Ctx &ctx) : IRCompiler(ctx) {
int tvsz = 100;
tv = new ld_plugin_tv[tvsz];
initializeTv();
+}
+
+GccIRCompiler::~GccIRCompiler() {
+ singleton = nullptr;
+ delete[] tv;
+}
+
+void GccIRCompiler::loadPlugin() {
plugin = dlopen(ctx.arg.plugin.data(), RTLD_NOW);
if (plugin == NULL) {
error(dlerror());
@@ -466,12 +475,74 @@ GccIRCompiler::GccIRCompiler(Ctx &ctx) : IRCompiler(ctx) {
std::memcpy(&onload, &tmp, sizeof(ld_plugin_onload));
(*onload)(tv);
+}
+enum ld_plugin_status regClaimFile(ld_plugin_claim_file_handler handler) {
+ GccIRCompiler *c = GccIRCompiler::getInstance();
+ return c->registerClaimFile(handler);
}
-GccIRCompiler::~GccIRCompiler() {
- delete tv;
- singleton = nullptr;
+enum ld_plugin_status
+GccIRCompiler::registerClaimFile(ld_plugin_claim_file_handler handler) {
+ claimFileHandler = handler;
+ return LDPS_OK;
+}
+
+#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
+enum ld_plugin_status regClaimFileV2(ld_plugin_claim_file_handler handler) {
+ GccIRCompiler *c = GccIRCompiler::getInstance();
+ return c->registerClaimFileV2(handler);
+}
+
+enum ld_plugin_status
+GccIRCompiler::registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler) {
+ claimFileHandlerV2 = handler;
+ return LDPS_OK;
+}
+#endif
+
+enum ld_plugin_status regAllSymbolsRead(ld_plugin_all_symbols_read_handler handler) {
+ GccIRCompiler *c = GccIRCompiler::getInstance();
+ return c->registerAllSymbolsRead(handler);
+}
+
+enum ld_plugin_status
+GccIRCompiler::registerAllSymbolsRead(ld_plugin_all_symbols_read_handler handler) {
+ allSymbolsReadHandler = handler;
+ return LDPS_OK;
+}
+
+static enum ld_plugin_status addSymbols(void *handle, int nsyms,
+ const struct ld_plugin_symbol *syms) {
+ ELFFileBase *f = (ELFFileBase *) handle;
+ if(f == NULL)
+ return LDPS_ERR;
+
+ for (int i = 0; i < nsyms; i++) {
+ // TODO: Add symbols.
+ // TODO: Convert these symbosl into ArrayRef<lto::InputFile::Symbol> and
+ // ArrayRef<Symbol *> ?
+ }
+
+ return LDPS_OK;
+}
+
+static enum ld_plugin_status getSymbols(const void *handle, int nsyms,
+ struct ld_plugin_symbol *syms) {
+ for (int i = 0; i < nsyms; i++) {
+ syms[i].resolution = LDPR_UNDEF;
+ // TODO: Implement other scenarios.
+ }
+ return LDPS_OK;
+}
+
+ld_plugin_status addInputFile(const char *pathname) {
+ GccIRCompiler *c = GccIRCompiler::getInstance();
+
+ if (c->addCompiledFile(StringRef(pathname)))
+ return LDPS_OK;
+ else
+ return LDPS_ERR;
}
void GccIRCompiler::initializeTv() {
@@ -486,19 +557,101 @@ void GccIRCompiler::initializeTv() {
TVU_SETTAG(LDPT_MESSAGE, message, message);
TVU_SETTAG(LDPT_API_VERSION, val, LD_PLUGIN_API_VERSION);
+ for (std::string &s : ctx.arg.pluginOpt) {
+ TVU_SETTAG(LDPT_OPTION, string, s.c_str());
+ }
+ ld_plugin_output_file_type o;
+ if (ctx.arg.pie)
+ o = LDPO_PIE;
+ else if (ctx.arg.relocatable)
+ o = LDPO_REL;
+ else if (ctx.arg.shared)
+ o = LDPO_DYN;
+ else
+ o = LDPO_EXEC;
+ TVU_SETTAG(LDPT_LINKER_OUTPUT, val, o);
+ TVU_SETTAG(LDPT_OUTPUT_NAME, string, ctx.arg.outputFile.data());
+ // Share the address of a C wrapper that is API-compatible with
+ // plugin-api.h.
+ TVU_SETTAG(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file, regClaimFile);
+#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
+ TVU_SETTAG(LDPT_REGISTER_CLAIM_FILE_HOOK_V2, register_claim_file_v2,
+ regClaimFileV2);
+#endif
+
+ TVU_SETTAG(LDPT_ADD_SYMBOLS, add_symbols, addSymbols);
+ TVU_SETTAG(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK, register_all_symbols_read,
+ regAllSymbolsRead);
+ TVU_SETTAG(LDPT_GET_SYMBOLS, get_symbols, getSymbols);
+ TVU_SETTAG(LDPT_ADD_INPUT_FILE, add_input_file, addInputFile);
+}
+
+void GccIRCompiler::add(ELFFileBase &f) {
+ struct ld_plugin_input_file file;
+
+ std::string name = f.getName().str();
+ file.name = f.getName().data();
+ file.handle = const_cast<void *>(reinterpret_cast<const void *>(&f));
+
+ std::error_code ec = sys::fs::openFileForRead(name, file.fd);
+ if (ec) {
+ error("Cannot open file " + name + ": " + ec.message());
+ return;
+ }
+ file.offset = 0;
+ uint64_t size;
+ ec = sys::fs::file_size(name, size);
+ if (ec) {
+ error("Cannot get the size of file " + name + ": " + ec.message());
+ sys::fs::closeFile(file.fd);
+ return;
+ }
+ if (size > 0 && size <= INT_MAX)
+ file.filesize = size;
+
+ int claimed;
+#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
+ ld_plugin_status status = claimFileHandler(&file, &claimed, 1);
+#else
+ ld_plugin_status status = claimFileHandler(&file, &claimed);
+#endif
+
+ if (status != LDPS_OK)
+ error("liblto returned " + std::to_string(status));
+
+ ec = sys::fs::closeFile(file.fd);
+ if (ec) {
+ error(ec.message());
+ }
}
SmallVector<std::unique_ptr<InputFile>, 0> GccIRCompiler::compile() {
SmallVector<std::unique_ptr<InputFile>, 0> ret;
- // TODO: Implement this function.
+ ld_plugin_status status = allSymbolsReadHandler();
+ if (status != LDPS_OK)
+ error("The plugin returned an error after all symbols were read.");
+
+ for (auto& m : files) {
+ ret.push_back(createObjFile(ctx, m->getMemBufferRef()));
+ }
return ret;
}
void GccIRCompiler::addObject(IRFile &f,
- std::vector<llvm::lto::SymbolResolution> &r) {}
+ std::vector<llvm::lto::SymbolResolution> &r) {
+ // TODO: Implement this.
+}
enum ld_plugin_status GccIRCompiler::message(int level, const char *format,
...) {
// TODO: Implement this function.
return LDPS_OK;
}
+
+bool GccIRCompiler::addCompiledFile(StringRef path) {
+ std::optional<MemoryBufferRef> mbref = readFile(ctx, path);
+ if (!mbref)
+ return false;
+ files.push_back(std::move(MemoryBuffer::getMemBuffer(*mbref)));
+ return true;
+}
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 63b3ca7a36be5..edf5e1ed22942 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -21,6 +21,7 @@
#define LLD_ELF_LTO_H
#include "lld/Common/LLVM.h"
+#include "lld/config.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
@@ -86,13 +87,29 @@ class GccIRCompiler : public IRCompiler {
static GccIRCompiler *getInstance();
static GccIRCompiler *getInstance(Ctx &ctx);
+ void add(ELFFileBase &f);
SmallVector<std::unique_ptr<InputFile>, 0> compile() override;
static enum ld_plugin_status message(int level, const char *format, ...);
+ enum ld_plugin_status registerClaimFile(ld_plugin_claim_file_handler handler);
+#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
+ enum ld_plugin_status
+ registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler);
+#endif
+ enum ld_plugin_status
+ registerAllSymbolsRead(ld_plugin_all_symbols_read_handler handler);
+ void loadPlugin();
+ bool addCompiledFile(StringRef path);
private:
GccIRCompiler(Ctx &ctx);
+ std::vector<std::unique_ptr<MemoryBuffer>> files;
static GccIRCompiler *singleton;
struct ld_plugin_tv *tv;
+ ld_plugin_claim_file_handler claimFileHandler;
+#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
+ ld_plugin_claim_file_handler_v2 claimFileHandlerV2;
+#endif
+ ld_plugin_all_symbols_read_handler allSymbolsReadHandler;
// Handle for the shared library created via dlopen().
void *plugin;
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index e3a5cabb74a42..b50a46e72c1d7 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -799,9 +799,9 @@ def plugin_opt_eq_minus: J<"plugin-opt=-">,
HelpText<"Specify an LLVM option for compatibility with LLVMgold.so">;
def: J<"plugin-opt=thinlto">;
-// Ignore GCC collect2 LTO plugin related options. Note that we don't support
-// GCC LTO, but GCC collect2 passes these options even in non-LTO mode.
-def: J<"plugin-opt=-fresolution=">;
+// GCC collect2 LTO plugin related options.
+def plugin_opt_fresolution: J<"plugin-opt=-fresolution=">;
+// Ignore -pass-through because it is unnecessary.
def: J<"plugin-opt=-pass-through=">;
// This may be either an unhandled LLVMgold.so feature or GCC passed
// -plugin-opt=path/to/{liblto_plugin.so,lto-wrapper}
diff --git a/lld/include/lld/config.h.cmake b/lld/include/lld/config.h.cmake
new file mode 100644
index 0000000000000..3035fdbadabd8
--- /dev/null
+++ b/lld/include/lld/config.h.cmake
@@ -0,0 +1,10 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+// Include this header only under the lld source tree.
+// This is a private header.
+
+/* Define to 1 if plugin-api.h supports tv_register_claim_file_v2, and to 0 otherwise. */
+#cmakedefine01 HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
+
+#endif
>From 1c175be16c1a38b3f4edba2034558cfb379da88a Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 17:54:25 -0300
Subject: [PATCH 04/14] lld: Implement options to disable GNU LTO and GPLv3
linking
Add LLD_LINK_GPL3 and LLD_ENABLE_GNULTO in order to control if LLD can
link/load to GPLv3 code and enabling support for the GNU LTO format
supported by GCC.
---
lld/CMakeLists.txt | 8 ++++++++
lld/ELF/CMakeLists.txt | 2 +-
lld/ELF/Driver.cpp | 10 ++++++++++
lld/ELF/LTO.cpp | 2 ++
lld/ELF/LTO.h | 2 ++
lld/include/lld/config.h.cmake | 6 ++++++
6 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt
index 80e25204a65ee..9481d71b0ca57 100644
--- a/lld/CMakeLists.txt
+++ b/lld/CMakeLists.txt
@@ -177,6 +177,14 @@ if (LLD_DEFAULT_LD_LLD_IS_MINGW)
add_definitions("-DLLD_DEFAULT_LD_LLD_IS_MINGW=1")
endif()
+option(LLD_LINK_GPL3 "Allow LLD to link to GPLv3-licensed files")
+option(LLD_ENABLE_GNU_LTO "Enable support for GNU LTO plugin")
+if(!LLD_LINK_GPL3)
+ # Support for GNU LTO require linking to liblto_plugin.so from GCC which is
+ # licensed as GPLv3.
+ set(LLD_ENABLE_GNU_LTO FALSE)
+endif()
+
if (MSVC)
add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.'
add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header.
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index 66e898350aad5..82121cb6b2422 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -19,7 +19,7 @@ if(LLVM_ENABLE_ZSTD)
endif()
set(GNULTO_INCLUDE_DIR "" CACHE PATH "Additional directory, where CMake should search for plugin-api.h")
-if(LLD_ENABLE_GNULTO)
+if(LLD_ENABLE_GNU_LTO)
find_package(GNULTO REQUIRED)
endif()
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index ca365671da7c1..e0510edd59861 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1813,16 +1813,22 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
ErrAlways(ctx) << arg->getSpelling() << ": unknown plugin option '"
<< arg->getValue() << "'";
else if (!ctx.arg.plugin.empty())
+#if LLD_ENABLE_GNU_LTO
ctx.arg.pluginOpt.push_back(v.str());
+#else
+ ErrAlways(ctx) << arg->getSpelling() << " : support for GNU LTO is disabled";
+#endif
}
// Parse GCC collect2 options.
if (!ctx.arg.plugin.empty()) {
+#if LLD_ENABLE_GNU_LTO
StringRef v = args.getLastArgValue(OPT_plugin_opt_fresolution);
if (!v.empty()) {
ctx.arg.resolutionFile = v;
ctx.arg.pluginOpt.push_back(std::string("-fresolution=" + v.str()));
}
+#endif
}
ctx.arg.passPlugins = args::getStrings(args, OPT_load_pass_plugins);
@@ -2751,6 +2757,7 @@ void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
}
}
+#if LLD_ENABLE_GNU_LTO
template <class ELFT>
void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
llvm::TimeTraceScope timeScope("LTO");
@@ -2781,6 +2788,7 @@ void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
}
return;
}
+#endif
// The --wrap option is a feature to rename symbols so that you can write
// wrappers for existing functions. If you pass `--wrap=foo`, all
@@ -3341,8 +3349,10 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
const size_t numInputFilesBeforeLTO = ctx.driver.files.size();
if (ctx.arg.plugin.empty()) {
compileBitcodeFiles<ELFT>(skipLinkedOutput);
+#if LLD_ENABLE_GNU_LTO
} else {
compileGccIRFiles<ELFT>(skipLinkedOutput);
+#endif
}
// Symbol resolution finished. Report backward reference problems,
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index df05e5521a37a..dde63aa754975 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -427,6 +427,7 @@ void BitcodeCompiler::addObject(IRFile &f,
checkError(ctx.e, ltoObj->add(std::move(f.obj), r));
}
+#if LLD_ENABLE_GNU_LTO
GccIRCompiler *GccIRCompiler::singleton = nullptr;
GccIRCompiler *GccIRCompiler::getInstance() {
@@ -655,3 +656,4 @@ bool GccIRCompiler::addCompiledFile(StringRef path) {
files.push_back(std::move(MemoryBuffer::getMemBuffer(*mbref)));
return true;
}
+#endif
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index edf5e1ed22942..3dac2eddfd532 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -77,6 +77,7 @@ class BitcodeCompiler : public IRCompiler {
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
};
+#if LLD_ENABLE_GNU_LTO
class GccIRCompiler : public IRCompiler {
protected:
void addObject(IRFile &f,
@@ -115,6 +116,7 @@ class GccIRCompiler : public IRCompiler {
void initializeTv();
};
+#endif
} // namespace lld::elf
diff --git a/lld/include/lld/config.h.cmake b/lld/include/lld/config.h.cmake
index 3035fdbadabd8..4eef0e1ed7c93 100644
--- a/lld/include/lld/config.h.cmake
+++ b/lld/include/lld/config.h.cmake
@@ -4,6 +4,12 @@
// Include this header only under the lld source tree.
// This is a private header.
+/* Allow LLD to link to GPLv3-licensed files. */
+#cmakedefine01 LLD_LINK_GPL3
+
+/* Enable support for GNU LTO Format, i.e. use LLD to link GCC LTO files. */
+#cmakedefine01 LLD_ENABLE_GNU_LTO
+
/* Define to 1 if plugin-api.h supports tv_register_claim_file_v2, and to 0 otherwise. */
#cmakedefine01 HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
>From 4c05a1d807703cdf5f2ec996c7e206b1b5b0b584 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 17:54:25 -0300
Subject: [PATCH 05/14] lld: Add a test for GNU LTO
This test ensures that GCC and LLD are both used and the final binary
has GNU LTO.
---
lld/test/ELF/gnu-lto/hello.test | 28 ++++++++++++++++++++++++++++
lld/test/lit.cfg.py | 20 ++++++++++++++++++++
lld/test/lit.site.cfg.py.in | 2 ++
3 files changed, 50 insertions(+)
create mode 100644 lld/test/ELF/gnu-lto/hello.test
diff --git a/lld/test/ELF/gnu-lto/hello.test b/lld/test/ELF/gnu-lto/hello.test
new file mode 100644
index 0000000000000..273039284ddc4
--- /dev/null
+++ b/lld/test/ELF/gnu-lto/hello.test
@@ -0,0 +1,28 @@
+// REQUIRES: gnu_lto, gcc
+// Test if we can link a simple LTO'ed program.
+// Ensure that LLD was used by checking for the output from LLD_VERSION.
+
+// RUN: gcc -c -flto -x c %s -o %t.dynamic.o
+// RUN: gcc -fuse-ld=lld -flto %t.dynamic.o -o %t.dynamic
+// RUN: %t.dynamic | FileCheck %s
+// RUN: llvm-objdump -t -j .comment -s %t.dynamic | \
+// RUN: FileCheck --check-prefix=OBJDUMP %s
+
+// RUN: gcc -c -flto -x c -fPIE %s -o %t.pie.o
+// RUN: gcc -fuse-ld=lld -flto -pie %t.pie.o -o %t.pie
+// RUN: %t.pie | FileCheck %s
+// RUN: llvm-objdump -t -j .comment -s %t.pie \
+// RUN: | FileCheck --check-prefix=OBJDUMP %s
+
+#include <stdio.h>
+
+int main() {
+ printf("It works!\n");
+ return 0;
+}
+
+// CHECK: It works!
+
+// OBJDUMP: __gnu_lto
+// OBJDUMP: .LLD 1.0
+// OBJDUMP: GCC
diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py
index 336945729954e..1274033735f40 100644
--- a/lld/test/lit.cfg.py
+++ b/lld/test/lit.cfg.py
@@ -182,3 +182,23 @@
# ELF tests expect the default target for ld.lld to be ELF.
if config.ld_lld_default_mingw:
config.excludes.append("ELF")
+
+def prepend_path(path):
+ target_arch = getattr(config, 'target_arch', None)
+ name = 'PATH'
+ if config.operating_system == 'Windows':
+ sep = ';'
+ else:
+ sep = ':'
+ if name in config.environment:
+ config.environment[name] = path + sep + config.environment[name]
+ else:
+ config.environment[name] = path
+
+gcc_executable = lit.util.which("gcc", config.environment["PATH"])
+if gcc_executable:
+ config.available_features.add("gcc")
+
+if config.has_gnu_lto:
+ prepend_path(config.llvm_obj_root)
+ config.available_features.add("gnu_lto")
diff --git a/lld/test/lit.site.cfg.py.in b/lld/test/lit.site.cfg.py.in
index bb99976005543..847dfc1853342 100644
--- a/lld/test/lit.site.cfg.py.in
+++ b/lld/test/lit.site.cfg.py.in
@@ -26,6 +26,8 @@ config.ld_lld_default_mingw = @LLD_DEFAULT_LD_LLD_IS_MINGW@
config.build_examples = @LLVM_BUILD_EXAMPLES@
config.has_plugins = @LLVM_ENABLE_PLUGINS@
config.linked_bye_extension = @LLVM_BYE_LINK_INTO_TOOLS@
+config.has_gnu_lto = lit.util.pythonize_bool("@LLD_ENABLE_GNU_LTO@")
+config.operating_system = "@CMAKE_SYSTEM_NAME@"
import lit.llvm
lit.llvm.initialize(lit_config, config)
>From bd85d2aaf044bf1c554ec3fab1c2554e7114dc6f Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 18:12:40 -0300
Subject: [PATCH 06/14] Fix code formatting issues
---
lld/ELF/Driver.cpp | 5 +++--
lld/ELF/InputFiles.h | 4 ++--
lld/ELF/LTO.cpp | 15 ++++++++-------
lld/test/lit.cfg.py | 11 ++++++-----
4 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index e0510edd59861..fdd6620aebbd9 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1814,9 +1814,10 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
<< arg->getValue() << "'";
else if (!ctx.arg.plugin.empty())
#if LLD_ENABLE_GNU_LTO
- ctx.arg.pluginOpt.push_back(v.str());
+ ctx.arg.pluginOpt.push_back(v.str());
#else
- ErrAlways(ctx) << arg->getSpelling() << " : support for GNU LTO is disabled";
+ ErrAlways(ctx) << arg->getSpelling()
+ << " : support for GNU LTO is disabled";
#endif
}
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 551fbb85cb84f..6678be65f6942 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -324,8 +324,8 @@ template <class ELFT> class ObjFile : public ELFFileBase {
class IRFile : public InputFile {
public:
- IRFile(Ctx &ctx, MemoryBufferRef m, StringRef archiveName, uint64_t offsetInArchive,
- bool lazy);
+ IRFile(Ctx &ctx, MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive, bool lazy);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
virtual void parse() = 0;
void parseLazy();
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index dde63aa754975..d75827301c02c 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -430,7 +430,7 @@ void BitcodeCompiler::addObject(IRFile &f,
#if LLD_ENABLE_GNU_LTO
GccIRCompiler *GccIRCompiler::singleton = nullptr;
- GccIRCompiler *GccIRCompiler::getInstance() {
+GccIRCompiler *GccIRCompiler::getInstance() {
assert(singleton != nullptr);
return singleton;
}
@@ -502,21 +502,22 @@ GccIRCompiler::registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler) {
}
#endif
-enum ld_plugin_status regAllSymbolsRead(ld_plugin_all_symbols_read_handler handler) {
+enum ld_plugin_status
+regAllSymbolsRead(ld_plugin_all_symbols_read_handler handler) {
GccIRCompiler *c = GccIRCompiler::getInstance();
return c->registerAllSymbolsRead(handler);
}
-enum ld_plugin_status
-GccIRCompiler::registerAllSymbolsRead(ld_plugin_all_symbols_read_handler handler) {
+enum ld_plugin_status GccIRCompiler::registerAllSymbolsRead(
+ ld_plugin_all_symbols_read_handler handler) {
allSymbolsReadHandler = handler;
return LDPS_OK;
}
static enum ld_plugin_status addSymbols(void *handle, int nsyms,
const struct ld_plugin_symbol *syms) {
- ELFFileBase *f = (ELFFileBase *) handle;
- if(f == NULL)
+ ELFFileBase *f = (ELFFileBase *)handle;
+ if (f == NULL)
return LDPS_ERR;
for (int i = 0; i < nsyms; i++) {
@@ -632,7 +633,7 @@ SmallVector<std::unique_ptr<InputFile>, 0> GccIRCompiler::compile() {
if (status != LDPS_OK)
error("The plugin returned an error after all symbols were read.");
- for (auto& m : files) {
+ for (auto &m : files) {
ret.push_back(createObjFile(ctx, m->getMemBufferRef()));
}
return ret;
diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py
index 1274033735f40..7cc72ecc9a3d7 100644
--- a/lld/test/lit.cfg.py
+++ b/lld/test/lit.cfg.py
@@ -184,17 +184,18 @@
config.excludes.append("ELF")
def prepend_path(path):
- target_arch = getattr(config, 'target_arch', None)
- name = 'PATH'
- if config.operating_system == 'Windows':
- sep = ';'
+ target_arch = getattr(config, "target_arch", None)
+ name = "PATH"
+ if config.operating_system == "Windows":
+ sep = ";"
else:
- sep = ':'
+ sep = ":"
if name in config.environment:
config.environment[name] = path + sep + config.environment[name]
else:
config.environment[name] = path
+
gcc_executable = lit.util.which("gcc", config.environment["PATH"])
if gcc_executable:
config.available_features.add("gcc")
>From 838a5deca8f8fc69b9b1504a54413b2e4e5919fa Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 5 Sep 2025 18:16:17 -0300
Subject: [PATCH 07/14] Fix a code formatting issue
---
lld/test/lit.cfg.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py
index 7cc72ecc9a3d7..38bbc28aa1d5d 100644
--- a/lld/test/lit.cfg.py
+++ b/lld/test/lit.cfg.py
@@ -183,6 +183,7 @@
if config.ld_lld_default_mingw:
config.excludes.append("ELF")
+
def prepend_path(path):
target_arch = getattr(config, "target_arch", None)
name = "PATH"
>From cfdce7afc097619175682b5b4230a8ac890f8ece Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Mon, 8 Sep 2025 18:01:01 -0300
Subject: [PATCH 08/14] Fix the build when plugin-api.h is not available
---
lld/ELF/LTO.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 3dac2eddfd532..21ce37f8b41b7 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -26,7 +26,6 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
-#include <plugin-api.h>
#include <vector>
namespace llvm::lto {
@@ -78,6 +77,8 @@ class BitcodeCompiler : public IRCompiler {
};
#if LLD_ENABLE_GNU_LTO
+#include <plugin-api.h>
+
class GccIRCompiler : public IRCompiler {
protected:
void addObject(IRFile &f,
>From 4b6454fe59c62566563fb7a4f9710252abc7ac24 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Mon, 29 Sep 2025 15:49:22 -0300
Subject: [PATCH 09/14] Fix warnings reported by the CI
---
lld/ELF/Driver.cpp | 3 ++-
lld/ELF/LTO.cpp | 2 +-
lld/ELF/LTO.h | 5 +++--
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index fdd6620aebbd9..4c0c283b9c94a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1788,11 +1788,12 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
// Ignore -plugin=LLVMgold.so because we don't need to load it.
StringRef v = args.getLastArgValue(OPT_plugin);
- if (!v.empty() && !v.ends_with("LLVMgold.so"))
+ if (!v.empty() && !v.ends_with("LLVMgold.so")) {
if (!llvm::sys::fs::exists(v))
ErrAlways(ctx) << "Cannot find plugin " << v;
else
ctx.arg.plugin = v;
+ }
// Parse LTO options.
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index d75827301c02c..f7d2ce39dff39 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -654,7 +654,7 @@ bool GccIRCompiler::addCompiledFile(StringRef path) {
std::optional<MemoryBufferRef> mbref = readFile(ctx, path);
if (!mbref)
return false;
- files.push_back(std::move(MemoryBuffer::getMemBuffer(*mbref)));
+ files.push_back(MemoryBuffer::getMemBuffer(*mbref));
return true;
}
#endif
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 21ce37f8b41b7..0d873b7cdd1eb 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -30,7 +30,7 @@
namespace llvm::lto {
class LTO;
-class SymbolResolution;
+struct SymbolResolution;
}
namespace lld::elf {
@@ -51,6 +51,7 @@ class IRCompiler {
public:
IRCompiler(Ctx &ctx) : ctx(ctx) {}
+ virtual ~IRCompiler() {};
void add(IRFile &f);
virtual SmallVector<std::unique_ptr<InputFile>, 0> compile() = 0;
};
@@ -62,7 +63,7 @@ class BitcodeCompiler : public IRCompiler {
public:
BitcodeCompiler(Ctx &ctx);
- ~BitcodeCompiler();
+ ~BitcodeCompiler() {};
void add(BinaryFile &f);
SmallVector<std::unique_ptr<InputFile>, 0> compile() override;
>From c651317d3cabc07de4582b9a5548a0030cab46d4 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Mon, 29 Sep 2025 17:15:20 -0300
Subject: [PATCH 10/14] Fix the build when dlfcn.h is not available
e.g. on Windows.
---
lld/ELF/CMakeLists.txt | 5 +++++
lld/ELF/LTO.cpp | 3 ++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index 82121cb6b2422..fc82c2da42b4e 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -21,6 +21,11 @@ endif()
set(GNULTO_INCLUDE_DIR "" CACHE PATH "Additional directory, where CMake should search for plugin-api.h")
if(LLD_ENABLE_GNU_LTO)
find_package(GNULTO REQUIRED)
+ include(CheckSymbolExists)
+ check_symbol_exists(dlopen "dlfcn.h" HAVE_DLOPEN)
+ if(NOT HAVE_DLOPEN)
+ message(FATAL_ERROR "Could not find the definition of dlopen(). Is dlfcn.h available?")
+ endif()
endif()
configure_file(
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index f7d2ce39dff39..51fc9cbdf194b 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -27,7 +27,6 @@
#include "llvm/Support/Path.h"
#include <cstddef>
#include <cstring>
-#include <dlfcn.h>
#include <memory>
#include <string>
#include <system_error>
@@ -428,6 +427,8 @@ void BitcodeCompiler::addObject(IRFile &f,
}
#if LLD_ENABLE_GNU_LTO
+#include <dlfcn.h>
+
GccIRCompiler *GccIRCompiler::singleton = nullptr;
GccIRCompiler *GccIRCompiler::getInstance() {
>From a8b6900331e27a02da568e308f8d41e0bb243630 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Tue, 30 Sep 2025 20:19:47 -0300
Subject: [PATCH 11/14] Improve the description of LLD_LINK_GPL3
Specify liblto_plugin.so and try to distinguish from the usual linking
that happens when linking to libstdc++ or libgcc_s.
---
lld/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt
index 9481d71b0ca57..b12e7bb74c6c5 100644
--- a/lld/CMakeLists.txt
+++ b/lld/CMakeLists.txt
@@ -177,7 +177,7 @@ if (LLD_DEFAULT_LD_LLD_IS_MINGW)
add_definitions("-DLLD_DEFAULT_LD_LLD_IS_MINGW=1")
endif()
-option(LLD_LINK_GPL3 "Allow LLD to link to GPLv3-licensed files")
+option(LLD_LINK_GPL3 "Allow LLD to link to GPLv3-licensed plugins, e.g. liblto_plugin.so")
option(LLD_ENABLE_GNU_LTO "Enable support for GNU LTO plugin")
if(!LLD_LINK_GPL3)
# Support for GNU LTO require linking to liblto_plugin.so from GCC which is
>From 56d0ccdfb3faff9f3d1030995792ec50b254833a Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 10 Oct 2025 18:04:14 -0300
Subject: [PATCH 12/14] Replace the usage of dlopen with
llvm::sys::DynamicLibrary
This will be helpful in order to support environments where dlfcn.h is
not available, e.g. Windows + mingw.
---
lld/ELF/CMakeLists.txt | 5 -----
lld/ELF/LTO.cpp | 13 ++++++-------
lld/ELF/LTO.h | 3 ++-
3 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index fc82c2da42b4e..82121cb6b2422 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -21,11 +21,6 @@ endif()
set(GNULTO_INCLUDE_DIR "" CACHE PATH "Additional directory, where CMake should search for plugin-api.h")
if(LLD_ENABLE_GNU_LTO)
find_package(GNULTO REQUIRED)
- include(CheckSymbolExists)
- check_symbol_exists(dlopen "dlfcn.h" HAVE_DLOPEN)
- if(NOT HAVE_DLOPEN)
- message(FATAL_ERROR "Could not find the definition of dlopen(). Is dlfcn.h available?")
- endif()
endif()
configure_file(
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 51fc9cbdf194b..9b150d6b85422 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -427,8 +427,6 @@ void BitcodeCompiler::addObject(IRFile &f,
}
#if LLD_ENABLE_GNU_LTO
-#include <dlfcn.h>
-
GccIRCompiler *GccIRCompiler::singleton = nullptr;
GccIRCompiler *GccIRCompiler::getInstance() {
@@ -460,13 +458,14 @@ GccIRCompiler::~GccIRCompiler() {
}
void GccIRCompiler::loadPlugin() {
- plugin = dlopen(ctx.arg.plugin.data(), RTLD_NOW);
- if (plugin == NULL) {
- error(dlerror());
+ std::string Error;
+ plugin = llvm::sys::DynamicLibrary::getPermanentLibrary(ctx.arg.plugin.data(), &Error);
+ if (!plugin.isValid()) {
+ error(Error);
return;
}
- void *tmp = dlsym(plugin, "onload");
- if (tmp == NULL) {
+ void *tmp = plugin.getAddressOfSymbol("onload");
+ if (!tmp) {
error("Plugin does not provide onload()");
return;
}
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 0d873b7cdd1eb..20f6fefabca36 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -25,6 +25,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/DynamicLibrary.h"
#include <memory>
#include <vector>
@@ -114,7 +115,7 @@ class GccIRCompiler : public IRCompiler {
#endif
ld_plugin_all_symbols_read_handler allSymbolsReadHandler;
// Handle for the shared library created via dlopen().
- void *plugin;
+ llvm::sys::DynamicLibrary plugin;
void initializeTv();
};
>From 330f25bf2ec7ed5daf575c1a11d9c1a4f651e872 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 10 Oct 2025 18:31:20 -0300
Subject: [PATCH 13/14] Remove unnecessary tests for -plugin
---
lld/test/ELF/plugin.test | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/lld/test/ELF/plugin.test b/lld/test/ELF/plugin.test
index e72ffd49339a6..25f9568019e58 100644
--- a/lld/test/ELF/plugin.test
+++ b/lld/test/ELF/plugin.test
@@ -1,20 +1,10 @@
RUN: not ld.lld -plugin %S/Inputs/plugin.so 2>&1 | FileCheck %s
-RUN: not ld.lld -plugin=%S/Inputs/plugin.so 2>&1 | FileCheck %s
-RUN: not ld.lld --plugin %S/Inputs/plugin.so 2>&1 | FileCheck %s
RUN: not ld.lld --plugin=%S/Inputs/plugin.so 2>&1 | FileCheck %s
-RUN: cd %S
-RUN: not ld.lld -plugin Inputs/plugin.so 2>&1 | FileCheck %s
-RUN: not ld.lld -plugin=Inputs/plugin.so 2>&1 | FileCheck %s
-RUN: not ld.lld --plugin Inputs/plugin.so 2>&1 | FileCheck %s
-RUN: not ld.lld --plugin=Inputs/plugin.so 2>&1 | FileCheck %s
-
CHECK: no input files
CHECK-NOT: unknown argument
RUN: not ld.lld -plugin foo 2>&1 | FileCheck --check-prefix=MISSING %s
-RUN: not ld.lld -plugin=foo 2>&1 | FileCheck --check-prefix=MISSING %s
-RUN: not ld.lld --plugin foo 2>&1 | FileCheck --check-prefix=MISSING %s
RUN: not ld.lld --plugin=foo 2>&1 | FileCheck --check-prefix=MISSING %s
MISSING: Cannot find plugin foo
>From b2d16d0f4f35edd9cd614399e7007056dbb933f9 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 10 Oct 2025 18:45:44 -0300
Subject: [PATCH 14/14] Fix code format
---
lld/ELF/LTO.cpp | 3 ++-
lld/ELF/LTO.h | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 9b150d6b85422..4068f04599834 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -459,7 +459,8 @@ GccIRCompiler::~GccIRCompiler() {
void GccIRCompiler::loadPlugin() {
std::string Error;
- plugin = llvm::sys::DynamicLibrary::getPermanentLibrary(ctx.arg.plugin.data(), &Error);
+ plugin = llvm::sys::DynamicLibrary::getPermanentLibrary(ctx.arg.plugin.data(),
+ &Error);
if (!plugin.isValid()) {
error(Error);
return;
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 20f6fefabca36..36e37b1c60e34 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -24,8 +24,8 @@
#include "lld/config.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>
More information about the llvm-commits
mailing list