[llvm] [gold] Handle duplicate archives gracefully in LTO plugin (PR #190934)
Kito Cheng via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 8 01:54:55 PDT 2026
https://github.com/kito-cheng created https://github.com/llvm/llvm-project/pull/190934
When the same archive is specified multiple times on the command line (a common pattern for resolving circular dependencies, e.g. "ld -la -lb -la"), the gold LTO plugin crashed with an assertion failure because duplicate archive members produced identical identifiers in ObjectToIndexFileState.
The assert(ObjFilename.second) introduced in 0c6a4ff8dcd3 assumed each identifier would be unique, which holds for a single pass over the inputs but breaks when the same archive appears more than once.
Fix this by calling getSymbolsAndView() before the duplicate check so that get_symbols is invoked for every claimed file (required by gold internals), then skipping duplicate modules via continue instead of asserting.
Add a regression test covering both Full LTO and ThinLTO modes.
>From 39bac4586b9bfc0aa070d4382613be05b3896f6f Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Wed, 8 Apr 2026 01:41:43 -0700
Subject: [PATCH] [gold] Handle duplicate archives gracefully in LTO plugin
When the same archive is specified multiple times on the command line
(a common pattern for resolving circular dependencies, e.g. "ld -la -lb
-la"), the gold LTO plugin crashed with an assertion failure because
duplicate archive members produced identical identifiers in
ObjectToIndexFileState.
The assert(ObjFilename.second) introduced in 0c6a4ff8dcd3 assumed each
identifier would be unique, which holds for a single pass over the
inputs but breaks when the same archive appears more than once.
Fix this by calling getSymbolsAndView() before the duplicate check so
that get_symbols is invoked for every claimed file (required by gold
internals), then skipping duplicate modules via continue instead of
asserting.
Add a regression test covering both Full LTO and ThinLTO modes.
---
.../gold/X86/Inputs/duplicate-archive.ll | 7 ++++
llvm/test/tools/gold/X86/duplicate-archive.ll | 35 +++++++++++++++++++
llvm/tools/gold/gold-plugin.cpp | 11 ++++--
3 files changed, 51 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/tools/gold/X86/Inputs/duplicate-archive.ll
create mode 100644 llvm/test/tools/gold/X86/duplicate-archive.ll
diff --git a/llvm/test/tools/gold/X86/Inputs/duplicate-archive.ll b/llvm/test/tools/gold/X86/Inputs/duplicate-archive.ll
new file mode 100644
index 0000000000000..c6c2046b31a2d
--- /dev/null
+++ b/llvm/test/tools/gold/X86/Inputs/duplicate-archive.ll
@@ -0,0 +1,7 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @foo() {
+entry:
+ ret void
+}
diff --git a/llvm/test/tools/gold/X86/duplicate-archive.ll b/llvm/test/tools/gold/X86/duplicate-archive.ll
new file mode 100644
index 0000000000000..b22606ebf7a01
--- /dev/null
+++ b/llvm/test/tools/gold/X86/duplicate-archive.ll
@@ -0,0 +1,35 @@
+; Test that passing the same archive twice to the LTO gold plugin does not
+; crash. This is a common pattern used to resolve circular dependencies
+; between archives (e.g., "ld -la -lb -la"). Previously this triggered an
+; assertion failure in gold-plugin.cpp because duplicate archive members
+; produced identical module identifiers in ObjectToIndexFileState.
+
+; RUN: llvm-as %s -o %t.o
+; RUN: llvm-as %p/Inputs/duplicate-archive.ll -o %t2.o
+; RUN: llvm-ar rcs %t.a %t2.o
+
+; Full LTO: same archive passed twice should not crash.
+; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext \
+; RUN: -m elf_x86_64 -shared \
+; RUN: -o %t.so %t.o %t.a %t.a
+; RUN: llvm-nm %t.so | FileCheck %s
+
+; ThinLTO: same archive passed twice should also not crash.
+; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext \
+; RUN: -m elf_x86_64 -shared \
+; RUN: --plugin-opt=thinlto \
+; RUN: -o %t.thinlto.so %t.o %t.a %t.a
+; RUN: llvm-nm %t.thinlto.so | FileCheck %s
+
+; CHECK-DAG: T foo
+; CHECK-DAG: T bar
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @foo()
+
+define void @bar() {
+ call void @foo()
+ ret void
+}
diff --git a/llvm/tools/gold/gold-plugin.cpp b/llvm/tools/gold/gold-plugin.cpp
index ba2a1699aaea9..c8b9be590e0ba 100644
--- a/llvm/tools/gold/gold-plugin.cpp
+++ b/llvm/tools/gold/gold-plugin.cpp
@@ -1083,8 +1083,15 @@ static std::vector<std::pair<SmallString<128>, bool>> runLTO() {
std::string Identifier =
getThinLTOObjectFileName(F.name, OldSuffix, NewSuffix);
auto ObjFilename = ObjectToIndexFileState.insert({Identifier, false});
- assert(ObjFilename.second);
- if (const void *View = getSymbolsAndView(F))
+ // get_symbols must be called for every claimed file; skipping it causes
+ // gold to crash when processing the file handle during linking.
+ const void *View = getSymbolsAndView(F);
+ // The same archive may be specified multiple times on the command line to
+ // handle circular dependencies between archives. Skip duplicate modules
+ // rather than asserting, since each module only needs to be processed once.
+ if (!ObjFilename.second)
+ continue;
+ if (View)
addModule(*Lto, F, View, ObjFilename.first->first());
else if (options::thinlto_index_only) {
ObjFilename.first->second = true;
More information about the llvm-commits
mailing list