[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 Dec 12 13:15:45 PST 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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>
 

>From 6de1cf92246d2964a9b72a998b917398c185bf35 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Tue, 14 Oct 2025 17:01:06 -0300
Subject: [PATCH 15/23] Remove code execution from hello.test

This could cause crashes on cross compilation environments.
Replace it with a check if the main function is indeed listed.
---
 lld/test/ELF/gnu-lto/hello.test | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/lld/test/ELF/gnu-lto/hello.test b/lld/test/ELF/gnu-lto/hello.test
index 273039284ddc4..267c5e20ecf9d 100644
--- a/lld/test/ELF/gnu-lto/hello.test
+++ b/lld/test/ELF/gnu-lto/hello.test
@@ -4,15 +4,17 @@
 
 // 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: llvm-readelf -s %t.dynamic | \
+// RUN:   FileCheck --check-prefix=READELF %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
+// RUN: llvm-readelf -s %t.pie | \
+// RUN:   FileCheck --check-prefix=READELF %s
 
 #include <stdio.h>
 
@@ -21,8 +23,8 @@ int main() {
   return 0;
 }
 
-// CHECK: It works!
-
 // OBJDUMP: __gnu_lto
 // OBJDUMP: .LLD 1.0
 // OBJDUMP: GCC
+
+// READELF: FUNC GLOBAL DEFAULT {{.*}} main

>From 8ce8149b8b40d5c20cf1881d5fd3de639d81596a Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 31 Oct 2025 17:10:17 -0300
Subject: [PATCH 16/23] Replace PATH manipulation on tests with -B

GCC provides parameter -B in order to achieve the same results.
---
 lld/test/ELF/gnu-lto/hello.test |  4 ++--
 lld/test/lit.cfg.py             | 17 ++---------------
 2 files changed, 4 insertions(+), 17 deletions(-)

diff --git a/lld/test/ELF/gnu-lto/hello.test b/lld/test/ELF/gnu-lto/hello.test
index 267c5e20ecf9d..af990c11acf5c 100644
--- a/lld/test/ELF/gnu-lto/hello.test
+++ b/lld/test/ELF/gnu-lto/hello.test
@@ -3,14 +3,14 @@
 // 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: gcc -B%llvm_obj_root -fuse-ld=lld -flto %t.dynamic.o -o %t.dynamic
 // RUN: llvm-objdump -t -j .comment -s %t.dynamic | \
 // RUN:   FileCheck --check-prefix=OBJDUMP %s
 // RUN: llvm-readelf -s %t.dynamic | \
 // RUN:   FileCheck --check-prefix=READELF %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: gcc -B%llvm_obj_root -fuse-ld=lld -flto -pie %t.pie.o -o %t.pie
 // RUN: llvm-objdump -t -j .comment -s %t.pie \
 // RUN:   | FileCheck --check-prefix=OBJDUMP %s
 // RUN: llvm-readelf -s %t.pie | \
diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py
index 38bbc28aa1d5d..eddc10ccb8bb6 100644
--- a/lld/test/lit.cfg.py
+++ b/lld/test/lit.cfg.py
@@ -183,24 +183,11 @@
 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")
+
+config.substitutions.append(("%llvm_obj_root", config.llvm_obj_root))

>From 01c0fc109f26349a554973dc0b78de42e16e70be Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 31 Oct 2025 17:11:33 -0300
Subject: [PATCH 17/23] Replace raw pointer usage with SmallVector

---
 lld/ELF/LTO.cpp | 16 +++++-----------
 lld/ELF/LTO.h   |  2 +-
 2 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 4068f04599834..698d8d7c191ce 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -445,16 +445,11 @@ GccIRCompiler *GccIRCompiler::getInstance(Ctx &ctx) {
 
 GccIRCompiler::GccIRCompiler(Ctx &ctx) : IRCompiler(ctx) {
   singleton = nullptr;
-
-  // TODO: Properly find the right size.
-  int tvsz = 100;
-  tv = new ld_plugin_tv[tvsz];
   initializeTv();
 }
 
 GccIRCompiler::~GccIRCompiler() {
   singleton = nullptr;
-  delete[] tv;
 }
 
 void GccIRCompiler::loadPlugin() {
@@ -476,7 +471,7 @@ void GccIRCompiler::loadPlugin() {
   assert(sizeof(ld_plugin_onload) == sizeof(void *));
   std::memcpy(&onload, &tmp, sizeof(ld_plugin_onload));
 
-  (*onload)(tv);
+  (*onload)(tv.data());
 }
 
 enum ld_plugin_status regClaimFile(ld_plugin_claim_file_handler handler) {
@@ -549,13 +544,12 @@ ld_plugin_status addInputFile(const char *pathname) {
 }
 
 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++;                                                                       \
+    ld_plugin_tv a;                                                            \
+    a.tv_tag = t;                                                              \
+    a.tv_u.tv_##f = v;                                                         \
+    tv.push_back(a);                                                           \
   }
 
   TVU_SETTAG(LDPT_MESSAGE, message, message);
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 36e37b1c60e34..0535a9eb5aa25 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -108,7 +108,7 @@ class GccIRCompiler : public IRCompiler {
   GccIRCompiler(Ctx &ctx);
   std::vector<std::unique_ptr<MemoryBuffer>> files;
   static GccIRCompiler *singleton;
-  struct ld_plugin_tv *tv;
+  SmallVector<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;

>From 29222568988887732909b66dda437d8b63be21c1 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 31 Oct 2025 17:32:52 -0300
Subject: [PATCH 18/23] Remove wrappers regClaimFile and regClaimFileV2

---
 lld/ELF/LTO.cpp | 20 ++++++--------------
 lld/ELF/LTO.h   |  4 ++--
 2 files changed, 8 insertions(+), 16 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 698d8d7c191ce..12d0136c3b353 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -474,26 +474,18 @@ void GccIRCompiler::loadPlugin() {
   (*onload)(tv.data());
 }
 
-enum ld_plugin_status regClaimFile(ld_plugin_claim_file_handler handler) {
-  GccIRCompiler *c = GccIRCompiler::getInstance();
-  return c->registerClaimFile(handler);
-}
-
 enum ld_plugin_status
 GccIRCompiler::registerClaimFile(ld_plugin_claim_file_handler handler) {
-  claimFileHandler = handler;
+  GccIRCompiler *c = GccIRCompiler::getInstance();
+  c->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;
+  GccIRCompiler *c = GccIRCompiler::getInstance();
+  c->claimFileHandlerV2 = handler;
   return LDPS_OK;
 }
 #endif
@@ -570,9 +562,9 @@ void GccIRCompiler::initializeTv() {
   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);
+  TVU_SETTAG(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file, registerClaimFile);
 #if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
-  TVU_SETTAG(LDPT_REGISTER_CLAIM_FILE_HOOK_V2, register_claim_file_v2,
+  TVU_SETTAG(LDPT_REGISTER_CLAIM_FILE_HOOK_V2, registerClaimFileV2,
              regClaimFileV2);
 #endif
 
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 0535a9eb5aa25..1707e37ab3d74 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -94,10 +94,10 @@ class GccIRCompiler : public IRCompiler {
   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);
+  static 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);
+  static registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler);
 #endif
   enum ld_plugin_status
   registerAllSymbolsRead(ld_plugin_all_symbols_read_handler handler);

>From c97f9f359ec79b8fd53991a913fc5f6ba0c0afd1 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Wed, 10 Dec 2025 11:09:43 -0300
Subject: [PATCH 19/23] Replace error() with Err(ctx)

---
 lld/ELF/LTO.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 12d0136c3b353..56381ea52c912 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -457,12 +457,12 @@ void GccIRCompiler::loadPlugin() {
   plugin = llvm::sys::DynamicLibrary::getPermanentLibrary(ctx.arg.plugin.data(),
                                                           &Error);
   if (!plugin.isValid()) {
-    error(Error);
+    Err(ctx) << Error;
     return;
   }
   void *tmp = plugin.getAddressOfSymbol("onload");
   if (!tmp) {
-    error("Plugin does not provide onload()");
+    Err(ctx) << "Plugin does not provide onload()";
     return;
   }
 
@@ -584,14 +584,14 @@ void GccIRCompiler::add(ELFFileBase &f) {
 
   std::error_code ec = sys::fs::openFileForRead(name, file.fd);
   if (ec) {
-    error("Cannot open file " + name + ": " + ec.message());
+    Err(ctx) << "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());
+    Err(ctx) << "Cannot get the size of file " + name + ": " + ec.message();
     sys::fs::closeFile(file.fd);
     return;
   }
@@ -606,11 +606,11 @@ void GccIRCompiler::add(ELFFileBase &f) {
 #endif
 
   if (status != LDPS_OK)
-    error("liblto returned " + std::to_string(status));
+    Err(ctx) << "liblto returned " + std::to_string(status);
 
   ec = sys::fs::closeFile(file.fd);
   if (ec) {
-    error(ec.message());
+    Err(ctx) << ec.message();
   }
 }
 
@@ -618,7 +618,7 @@ SmallVector<std::unique_ptr<InputFile>, 0> GccIRCompiler::compile() {
   SmallVector<std::unique_ptr<InputFile>, 0> ret;
   ld_plugin_status status = allSymbolsReadHandler();
   if (status != LDPS_OK)
-    error("The plugin returned an error after all symbols were read.");
+    Err(ctx) << "The plugin returned an error after all symbols were read.";
 
   for (auto &m : files) {
     ret.push_back(createObjFile(ctx, m->getMemBufferRef()));

>From 583486029a6dac88a3b79d67d786508840438b2a Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Wed, 10 Dec 2025 11:12:44 -0300
Subject: [PATCH 20/23] Remove unnecessary semicolons

---
 lld/ELF/LTO.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 1707e37ab3d74..660dde758abd5 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -52,7 +52,7 @@ class IRCompiler {
 
 public:
   IRCompiler(Ctx &ctx) : ctx(ctx) {}
-  virtual ~IRCompiler() {};
+  virtual ~IRCompiler() {}
   void add(IRFile &f);
   virtual SmallVector<std::unique_ptr<InputFile>, 0> compile() = 0;
 };
@@ -64,7 +64,7 @@ class BitcodeCompiler : public IRCompiler {
 
 public:
   BitcodeCompiler(Ctx &ctx);
-  ~BitcodeCompiler() {};
+  ~BitcodeCompiler() {}
 
   void add(BinaryFile &f);
   SmallVector<std::unique_ptr<InputFile>, 0> compile() override;

>From 34da13ab86dc93eefa404ac2d98ff529708bcc89 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Wed, 10 Dec 2025 11:46:32 -0300
Subject: [PATCH 21/23] Reuse code from compileBitcodeFiles in
 compileGCCIRFiles

Create a new function called compileFiles that can be reused both in
compileBitcodeFiles() and compileGCCIRFiles().
---
 lld/ELF/Config.h   |  1 +
 lld/ELF/Driver.cpp | 60 +++++++++++++++++++---------------------------
 2 files changed, 25 insertions(+), 36 deletions(-)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index bdb7bcc097441..2e04648ba4540 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -191,6 +191,7 @@ class LinkerDriver {
   void createFiles(llvm::opt::InputArgList &args);
   void inferMachineType();
   template <class ELFT> void link(llvm::opt::InputArgList &args);
+  template <class ELFT> void compileFiles();
   template <class ELFT> void compileBitcodeFiles(bool skipLinkedOutput);
   template <class ELFT> void compileGccIRFiles(bool skipLinkedOutput);
   bool tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName,
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 55cae162303fe..c662c3a1fecdd 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2715,24 +2715,8 @@ static void markBuffersAsDontNeed(Ctx &ctx, bool skipLinkedOutput) {
       mb.dontNeedIfMmap();
 }
 
-// This function is where all the optimizations of link-time
-// 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.
-// Because all bitcode files that the program consists of are passed to
-// the compiler at once, it can do a whole-program optimization.
 template <class ELFT>
-void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
-  llvm::TimeTraceScope timeScope("LTO");
-  // Compile bitcode files and replace bitcode symbols.
-  lto.reset(new BitcodeCompiler(ctx));
-  for (BitcodeFile *file : ctx.bitcodeFiles)
-    lto->add(*file);
-
-  if (!ctx.bitcodeFiles.empty())
-    markBuffersAsDontNeed(ctx, skipLinkedOutput);
-
+void LinkerDriver::compileFiles() {
   ltoObjectFiles = lto->compile();
   for (auto &file : ltoObjectFiles) {
     auto *obj = cast<ObjFile<ELFT>>(file.get());
@@ -2765,6 +2749,28 @@ void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
   }
 }
 
+
+// This function is where all the optimizations of link-time
+// 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.
+// Because all bitcode files that the program consists of are passed to
+// the compiler at once, it can do a whole-program optimization.
+template <class ELFT>
+void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
+  llvm::TimeTraceScope timeScope("LTO");
+  // Compile bitcode files and replace bitcode symbols.
+  lto.reset(new BitcodeCompiler(ctx));
+  for (BitcodeFile *file : ctx.bitcodeFiles)
+    lto->add(*file);
+
+  if (!ctx.bitcodeFiles.empty())
+    markBuffersAsDontNeed(ctx, skipLinkedOutput);
+
+  compileFiles<ELFT>();
+}
+
 #if LLD_ENABLE_GNU_LTO
 template <class ELFT>
 void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
@@ -2776,25 +2782,7 @@ void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
   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;
+  compileFiles<ELFT>();
 }
 #endif
 

>From 129978c8434f9ef9b2b259891e2e67835bf421ff Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Wed, 10 Dec 2025 12:33:01 -0300
Subject: [PATCH 22/23] Replace the singleton with a global variable

---
 lld/ELF/Driver.cpp |  2 +-
 lld/ELF/LTO.cpp    | 35 +++++++++--------------------------
 lld/ELF/LTO.h      |  5 +----
 3 files changed, 11 insertions(+), 31 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index c662c3a1fecdd..2c9f61463b1dd 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2776,7 +2776,7 @@ template <class ELFT>
 void LinkerDriver::compileGccIRFiles(bool skipLinkedOutput) {
   llvm::TimeTraceScope timeScope("LTO");
   // Compile files and replace symbols.
-  GccIRCompiler *c = GccIRCompiler::getInstance(ctx);
+  GccIRCompiler *c = new GccIRCompiler(ctx);
   lto.reset(c);
 
   for (ELFFileBase *file : ctx.objectFiles)
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 56381ea52c912..657f0d46b8faf 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -427,29 +427,17 @@ void BitcodeCompiler::addObject(IRFile &f,
 }
 
 #if LLD_ENABLE_GNU_LTO
-GccIRCompiler *GccIRCompiler::singleton = nullptr;
-
-GccIRCompiler *GccIRCompiler::getInstance() {
-  assert(singleton != nullptr);
-  return singleton;
-}
-
-GccIRCompiler *GccIRCompiler::getInstance(Ctx &ctx) {
-  if (singleton == nullptr) {
-    singleton = new GccIRCompiler(ctx);
-    singleton->loadPlugin();
-  }
-
-  return singleton;
-}
+GccIRCompiler *gcc = nullptr;
 
 GccIRCompiler::GccIRCompiler(Ctx &ctx) : IRCompiler(ctx) {
-  singleton = nullptr;
   initializeTv();
+  assert(gcc == nullptr);
+  gcc = this;
+  loadPlugin();
 }
 
 GccIRCompiler::~GccIRCompiler() {
-  singleton = nullptr;
+  gcc = nullptr;
 }
 
 void GccIRCompiler::loadPlugin() {
@@ -476,24 +464,21 @@ void GccIRCompiler::loadPlugin() {
 
 enum ld_plugin_status
 GccIRCompiler::registerClaimFile(ld_plugin_claim_file_handler handler) {
-  GccIRCompiler *c = GccIRCompiler::getInstance();
-  c->claimFileHandler = handler;
+  gcc->claimFileHandler = handler;
   return LDPS_OK;
 }
 
 #if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
 enum ld_plugin_status
 GccIRCompiler::registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler) {
-  GccIRCompiler *c = GccIRCompiler::getInstance();
-  c->claimFileHandlerV2 = handler;
+  gcc->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);
+  return gcc->registerAllSymbolsRead(handler);
 }
 
 enum ld_plugin_status GccIRCompiler::registerAllSymbolsRead(
@@ -527,9 +512,7 @@ static enum ld_plugin_status getSymbols(const void *handle, int nsyms,
 }
 
 ld_plugin_status addInputFile(const char *pathname) {
-  GccIRCompiler *c = GccIRCompiler::getInstance();
-
-  if (c->addCompiledFile(StringRef(pathname)))
+  if (gcc->addCompiledFile(StringRef(pathname)))
     return LDPS_OK;
   else
     return LDPS_ERR;
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index 660dde758abd5..c3296908a7d86 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -87,9 +87,8 @@ class GccIRCompiler : public IRCompiler {
                  std::vector<llvm::lto::SymbolResolution> &r) override;
 
 public:
+  GccIRCompiler(Ctx &ctx);
   ~GccIRCompiler();
-  static GccIRCompiler *getInstance();
-  static GccIRCompiler *getInstance(Ctx &ctx);
 
   void add(ELFFileBase &f);
   SmallVector<std::unique_ptr<InputFile>, 0> compile() override;
@@ -105,9 +104,7 @@ class GccIRCompiler : public IRCompiler {
   bool addCompiledFile(StringRef path);
 
 private:
-  GccIRCompiler(Ctx &ctx);
   std::vector<std::unique_ptr<MemoryBuffer>> files;
-  static GccIRCompiler *singleton;
   SmallVector<struct ld_plugin_tv> tv;
   ld_plugin_claim_file_handler claimFileHandler;
 #if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2

>From 2c610db859314e8d92ef476505a7ed571ca9f5d6 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom at redhat.com>
Date: Fri, 12 Dec 2025 18:05:20 -0300
Subject: [PATCH 23/23] Add GnuLTO.h as a replacement for plugin-api.h

Rewrite the API used by GCC in C++ allowing LLD to be built without
depending on plugin-api.h being installed on the system, which
simplifies the bootstrap of a toolchain.
---
 lld/ELF/CMakeLists.txt             |   5 -
 lld/ELF/GnuLTO.h                   | 167 +++++++++++++++++++++++++++++
 lld/ELF/LTO.cpp                    |  91 ++++++----------
 lld/ELF/LTO.h                      |  27 ++---
 lld/cmake/modules/FindGNULTO.cmake |  38 -------
 lld/include/lld/config.h.cmake     |   3 -
 6 files changed, 211 insertions(+), 120 deletions(-)
 create mode 100644 lld/ELF/GnuLTO.h
 delete mode 100644 lld/cmake/modules/FindGNULTO.cmake

diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index 82121cb6b2422..8d320ebee1a68 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -18,11 +18,6 @@ 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_GNU_LTO)
-  find_package(GNULTO REQUIRED)
-endif()
-
 configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/../include/lld/config.h.cmake
   ${CMAKE_CURRENT_BINARY_DIR}/../include/lld/config.h)
diff --git a/lld/ELF/GnuLTO.h b/lld/ELF/GnuLTO.h
new file mode 100644
index 0000000000000..68595b396f3b9
--- /dev/null
+++ b/lld/ELF/GnuLTO.h
@@ -0,0 +1,167 @@
+//===- LTO.h ----------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides a C++ interface to the GCC LTO format.
+//
+// This file must be kept in sync with plugin-api.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GNULTO_H
+#define GNULTO_H
+
+#include "lld/Common/LLVM.h"
+
+namespace lld::elf {
+
+enum PluginStatus {
+  LDPS_OK = 0,
+  LDPS_NO_SYMS,
+  LDPS_BAD_HANDLE,
+  LDPS_ERR,
+};
+
+enum PluginAPIVersion {
+  LD_PLUGIN_API_VERSION = 1,
+};
+
+enum PluginFileType {
+  LDPO_REL,
+  LDPO_EXEC,
+  LDPO_DYN,
+  LDPO_PIE,
+};
+
+struct PluginInputFile {
+  const char *name;
+  int fd;
+  off_t offset;
+  off_t filesize;
+  void *handle;
+};
+
+struct PluginSymbol {
+  char *name;
+  char *version;
+  /* This is for compatibility with older ABIs.  The older ABI defined
+     only 'def' field.  */
+#if __BIG_ENDIAN__ == 1
+  char unused;
+  char section_kind;
+  char symbol_type;
+  char def;
+#elif __LITTLE_ENDIAN__ == 1
+  char def;
+  char symbol_type;
+  char section_kind;
+  char unused;
+#else
+#error "Could not detect architecture endianess"
+#endif
+  int visibility;
+  uint64_t size;
+  char *comdat_key;
+  int resolution;
+};
+
+enum PluginSymbolResolution {
+  LDPR_UNKNOWN = 0,
+  LDPR_UNDEF,
+  LDPR_PREVAILING_DEF,
+  LDPR_PREVAILING_DEF_IRONLY,
+  LDPR_PREEMPTED_REG,
+  LDPR_PREEMPTED_IR,
+  LDPR_RESOLVED_IR,
+  LDPR_RESOLVED_EXEC,
+  LDPR_RESOLVED_DYN,
+  LDPR_PREVAILING_DEF_IRONLY_EXP,
+};
+
+enum PluginTag {
+  LDPT_NULL,
+  LDPT_API_VERSION,
+  LDPT_GOLD_VERSION,
+  LDPT_LINKER_OUTPUT,
+  LDPT_OPTION,
+  LDPT_REGISTER_CLAIM_FILE_HOOK,
+  LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
+  LDPT_REGISTER_CLEANUP_HOOK,
+  LDPT_ADD_SYMBOLS,
+  LDPT_GET_SYMBOLS,
+  LDPT_ADD_INPUT_FILE,
+  LDPT_MESSAGE,
+  LDPT_GET_INPUT_FILE,
+  LDPT_RELEASE_INPUT_FILE,
+  LDPT_ADD_INPUT_LIBRARY,
+  LDPT_OUTPUT_NAME,
+  LDPT_SET_EXTRA_LIBRARY_PATH,
+  LDPT_GNU_LD_VERSION,
+  LDPT_GET_VIEW,
+  LDPT_GET_INPUT_SECTION_COUNT,
+  LDPT_GET_INPUT_SECTION_TYPE,
+  LDPT_GET_INPUT_SECTION_NAME,
+  LDPT_GET_INPUT_SECTION_CONTENTS,
+  LDPT_UPDATE_SECTION_ORDER,
+  LDPT_ALLOW_SECTION_ORDERING,
+  LDPT_GET_SYMBOLS_V2,
+  LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
+  LDPT_UNIQUE_SEGMENT_FOR_SECTIONS,
+  LDPT_GET_SYMBOLS_V3,
+  LDPT_GET_INPUT_SECTION_ALIGNMENT,
+  LDPT_GET_INPUT_SECTION_SIZE,
+  LDPT_REGISTER_NEW_INPUT_HOOK,
+  LDPT_GET_WRAP_SYMBOLS,
+  LDPT_ADD_SYMBOLS_V2,
+  LDPT_GET_API_VERSION,
+  LDPT_REGISTER_CLAIM_FILE_HOOK_V2,
+};
+
+typedef PluginStatus pluginOnLoad(struct PluginTV *tv);
+typedef PluginStatus pluginClaimFileHandler(const PluginInputFile *file,
+                                            int *claimed);
+typedef PluginStatus pluginClaimFileHandlerV2(const PluginInputFile *file,
+                                              int *claimed, int known_used);
+typedef PluginStatus pluginAllSymbolsReadHandler();
+typedef PluginStatus pluginMessage(int level, const char *format, ...);
+typedef PluginStatus pluginRegisterClaimFile(pluginClaimFileHandler *handler);
+typedef PluginStatus
+pluginRegisterClaimFileV2(pluginClaimFileHandlerV2 *handler);
+typedef PluginStatus pluginAddSymbols(void *handle, int nsyms,
+                                      const PluginSymbol *syms);
+typedef PluginStatus
+pluginRegisterAllSymbolsRead(pluginAllSymbolsReadHandler *handler);
+typedef PluginStatus pluginGetSymbols(const void *handle, int nsyms,
+                                      PluginSymbol *syms);
+typedef PluginStatus pluginAddInputFile(const char *pathname);
+
+struct PluginTV {
+  PluginTV(PluginTag tag, int val) : tag(tag), val(val) {}
+  PluginTV(PluginTag tag, const char *ptr) : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginClaimFileHandler *ptr)
+      : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginMessage *ptr) : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginRegisterClaimFile *ptr)
+      : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginRegisterClaimFileV2 *ptr)
+      : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginAddSymbols *ptr) : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginRegisterAllSymbolsRead *ptr)
+      : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginGetSymbols *ptr) : tag(tag), ptr((void *)ptr) {}
+  PluginTV(PluginTag tag, pluginAddInputFile *ptr)
+      : tag(tag), ptr((void *)ptr) {}
+
+  PluginTag tag;
+  union {
+    int val;
+    void *ptr;
+  };
+};
+} // namespace lld::elf
+
+#endif // GNULTO_H
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 657f0d46b8faf..fe94215b27461 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -448,47 +448,38 @@ void GccIRCompiler::loadPlugin() {
     Err(ctx) << Error;
     return;
   }
-  void *tmp = plugin.getAddressOfSymbol("onload");
-  if (!tmp) {
+  pluginOnLoad *onload = (pluginOnLoad *)plugin.getAddressOfSymbol("onload");
+  if (!onload) {
     Err(ctx) << "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.data());
 }
 
-enum ld_plugin_status
-GccIRCompiler::registerClaimFile(ld_plugin_claim_file_handler handler) {
+PluginStatus GccIRCompiler::registerClaimFile(pluginClaimFileHandler handler) {
   gcc->claimFileHandler = handler;
   return LDPS_OK;
 }
 
-#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
-enum ld_plugin_status
-GccIRCompiler::registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler) {
+PluginStatus
+GccIRCompiler::registerClaimFileV2(pluginClaimFileHandlerV2 handler) {
   gcc->claimFileHandlerV2 = handler;
   return LDPS_OK;
 }
-#endif
 
-enum ld_plugin_status
-regAllSymbolsRead(ld_plugin_all_symbols_read_handler handler) {
+PluginStatus regAllSymbolsRead(pluginAllSymbolsReadHandler handler) {
   return gcc->registerAllSymbolsRead(handler);
 }
 
-enum ld_plugin_status GccIRCompiler::registerAllSymbolsRead(
-    ld_plugin_all_symbols_read_handler handler) {
+PluginStatus
+GccIRCompiler::registerAllSymbolsRead(pluginAllSymbolsReadHandler handler) {
   allSymbolsReadHandler = handler;
   return LDPS_OK;
 }
 
-static enum ld_plugin_status addSymbols(void *handle, int nsyms,
-                                        const struct ld_plugin_symbol *syms) {
+static PluginStatus addSymbols(void *handle, int nsyms,
+                               const struct PluginSymbol *syms) {
   ELFFileBase *f = (ELFFileBase *)handle;
   if (f == NULL)
     return LDPS_ERR;
@@ -502,8 +493,8 @@ static enum ld_plugin_status addSymbols(void *handle, int nsyms,
   return LDPS_OK;
 }
 
-static enum ld_plugin_status getSymbols(const void *handle, int nsyms,
-                                        struct ld_plugin_symbol *syms) {
+static PluginStatus getSymbols(const void *handle, int nsyms,
+                               struct PluginSymbol *syms) {
   for (int i = 0; i < nsyms; i++) {
     syms[i].resolution = LDPR_UNDEF;
     // TODO: Implement other scenarios.
@@ -511,7 +502,7 @@ static enum ld_plugin_status getSymbols(const void *handle, int nsyms,
   return LDPS_OK;
 }
 
-ld_plugin_status addInputFile(const char *pathname) {
+PluginStatus addInputFile(const char *pathname) {
   if (gcc->addCompiledFile(StringRef(pathname)))
     return LDPS_OK;
   else
@@ -519,20 +510,11 @@ ld_plugin_status addInputFile(const char *pathname) {
 }
 
 void GccIRCompiler::initializeTv() {
-#define TVU_SETTAG(t, f, v)                                                    \
-  {                                                                            \
-    ld_plugin_tv a;                                                            \
-    a.tv_tag = t;                                                              \
-    a.tv_u.tv_##f = v;                                                         \
-    tv.push_back(a);                                                           \
-  }
-
-  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;
+  tv.push_back(PluginTV(LDPT_MESSAGE, &message));
+  tv.push_back(PluginTV(LDPT_API_VERSION, LD_PLUGIN_API_VERSION));
+  for (std::string &s : ctx.arg.pluginOpt)
+    tv.push_back(PluginTV(LDPT_OPTION, s.c_str()));
+  PluginFileType o;
   if (ctx.arg.pie)
     o = LDPO_PIE;
   else if (ctx.arg.relocatable)
@@ -541,25 +523,23 @@ void GccIRCompiler::initializeTv() {
     o = LDPO_DYN;
   else
     o = LDPO_EXEC;
-  TVU_SETTAG(LDPT_LINKER_OUTPUT, val, o);
-  TVU_SETTAG(LDPT_OUTPUT_NAME, string, ctx.arg.outputFile.data());
+  tv.push_back(PluginTV(LDPT_LINKER_OUTPUT, o));
+  tv.push_back(PluginTV(LDPT_OUTPUT_NAME, 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, registerClaimFile);
-#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
-  TVU_SETTAG(LDPT_REGISTER_CLAIM_FILE_HOOK_V2, registerClaimFileV2,
-             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);
+  tv.push_back(PluginTV(LDPT_REGISTER_CLAIM_FILE_HOOK, &registerClaimFile));
+  tv.push_back(
+      PluginTV(LDPT_REGISTER_CLAIM_FILE_HOOK_V2, &registerClaimFileV2));
+
+  tv.push_back(PluginTV(LDPT_ADD_SYMBOLS, &addSymbols));
+  tv.push_back(
+      PluginTV(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK, &regAllSymbolsRead));
+  tv.push_back(PluginTV(LDPT_GET_SYMBOLS, &getSymbols));
+  tv.push_back(PluginTV(LDPT_ADD_INPUT_FILE, &addInputFile));
 }
 
 void GccIRCompiler::add(ELFFileBase &f) {
-  struct ld_plugin_input_file file;
+  PluginInputFile file;
 
   std::string name = f.getName().str();
   file.name = f.getName().data();
@@ -582,11 +562,7 @@ void GccIRCompiler::add(ELFFileBase &f) {
     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
+  PluginStatus status = claimFileHandlerV2(&file, &claimed, 1);
 
   if (status != LDPS_OK)
     Err(ctx) << "liblto returned " + std::to_string(status);
@@ -599,7 +575,7 @@ void GccIRCompiler::add(ELFFileBase &f) {
 
 SmallVector<std::unique_ptr<InputFile>, 0> GccIRCompiler::compile() {
   SmallVector<std::unique_ptr<InputFile>, 0> ret;
-  ld_plugin_status status = allSymbolsReadHandler();
+  PluginStatus status = allSymbolsReadHandler();
   if (status != LDPS_OK)
     Err(ctx) << "The plugin returned an error after all symbols were read.";
 
@@ -614,8 +590,7 @@ void GccIRCompiler::addObject(IRFile &f,
   // TODO: Implement this.
 }
 
-enum ld_plugin_status GccIRCompiler::message(int level, const char *format,
-                                             ...) {
+enum PluginStatus 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 c3296908a7d86..ac3acb9e283f4 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -26,6 +26,9 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/DynamicLibrary.h"
 #include "llvm/Support/raw_ostream.h"
+#if LLD_ENABLE_GNU_LTO
+#include "GnuLTO.h"
+#endif
 #include <memory>
 #include <vector>
 
@@ -79,8 +82,6 @@ class BitcodeCompiler : public IRCompiler {
 };
 
 #if LLD_ENABLE_GNU_LTO
-#include <plugin-api.h>
-
 class GccIRCompiler : public IRCompiler {
 protected:
   void addObject(IRFile &f,
@@ -92,25 +93,19 @@ class GccIRCompiler : public IRCompiler {
 
   void add(ELFFileBase &f);
   SmallVector<std::unique_ptr<InputFile>, 0> compile() override;
-  static enum ld_plugin_status message(int level, const char *format, ...);
-  static enum ld_plugin_status registerClaimFile(ld_plugin_claim_file_handler handler);
-#if HAVE_LDPT_REGISTER_CLAIM_FILE_HOOK_V2
-  enum ld_plugin_status
-  static registerClaimFileV2(ld_plugin_claim_file_handler_v2 handler);
-#endif
-  enum ld_plugin_status
-  registerAllSymbolsRead(ld_plugin_all_symbols_read_handler handler);
+  static PluginStatus message(int level, const char *format, ...);
+  static PluginStatus registerClaimFile(pluginClaimFileHandler handler);
+  static PluginStatus registerClaimFileV2(pluginClaimFileHandlerV2 handler);
+  PluginStatus registerAllSymbolsRead(pluginAllSymbolsReadHandler handler);
   void loadPlugin();
   bool addCompiledFile(StringRef path);
 
 private:
   std::vector<std::unique_ptr<MemoryBuffer>> files;
-  SmallVector<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;
+  SmallVector<PluginTV> tv;
+  pluginClaimFileHandler *claimFileHandler;
+  pluginClaimFileHandlerV2 *claimFileHandlerV2;
+  pluginAllSymbolsReadHandler *allSymbolsReadHandler;
   // Handle for the shared library created via dlopen().
   llvm::sys::DynamicLibrary plugin;
 
diff --git a/lld/cmake/modules/FindGNULTO.cmake b/lld/cmake/modules/FindGNULTO.cmake
deleted file mode 100644
index 2ba603bf489e2..0000000000000
--- a/lld/cmake/modules/FindGNULTO.cmake
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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)
diff --git a/lld/include/lld/config.h.cmake b/lld/include/lld/config.h.cmake
index 4eef0e1ed7c93..a67d7484f323e 100644
--- a/lld/include/lld/config.h.cmake
+++ b/lld/include/lld/config.h.cmake
@@ -10,7 +10,4 @@
 /* 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
-
 #endif



More information about the llvm-commits mailing list