[lld][ELF] Error when deplibs adds new input file after LTO (#98565)

Parsing the new input file's symbols might invalidate LTO codegen, but
the semantics of deplibs require them to be parsed. Accordingly, report
an error unless the file had already been added to the link.

Fixes #56070
This commit is contained in:
Daniel Thornburgh 2024-07-12 12:43:53 -07:00 committed by GitHub
parent 7f06560edb
commit 5b82741a01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 0 deletions

View File

@ -2966,6 +2966,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// With this the symbol table should be complete. After this, no new names // With this the symbol table should be complete. After this, no new names
// except a few linker-synthesized ones will be added to the symbol table. // except a few linker-synthesized ones will be added to the symbol table.
const size_t numObjsBeforeLTO = ctx.objectFiles.size(); const size_t numObjsBeforeLTO = ctx.objectFiles.size();
const size_t numInputFilesBeforeLTO = ctx.driver.files.size();
compileBitcodeFiles<ELFT>(skipLinkedOutput); compileBitcodeFiles<ELFT>(skipLinkedOutput);
// Symbol resolution finished. Report backward reference problems, // Symbol resolution finished. Report backward reference problems,
@ -2990,6 +2991,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
for (const DuplicateSymbol &d : ctx.duplicates) for (const DuplicateSymbol &d : ctx.duplicates)
reportDuplicate(*d.sym, d.file, d.section, d.value); reportDuplicate(*d.sym, d.file, d.section, d.value);
// ELF dependent libraries may have introduced new input files after LTO has
// completed. This is an error if the files haven't already been parsed, since
// changing the symbol table could break the semantic assumptions of LTO.
auto newInputFiles = ArrayRef(ctx.driver.files).slice(numInputFilesBeforeLTO);
if (!newInputFiles.empty()) {
DenseSet<StringRef> oldFilenames;
for (InputFile *f :
ArrayRef(ctx.driver.files).slice(0, numInputFilesBeforeLTO))
oldFilenames.insert(f->getName());
for (InputFile *newFile : newInputFiles)
if (!oldFilenames.contains(newFile->getName()))
errorOrWarn("input file '" + newFile->getName() + "' added after LTO");
}
// Handle --exclude-libs again because lto.tmp may reference additional // Handle --exclude-libs again because lto.tmp may reference additional
// libcalls symbols defined in an excluded archive. This may override // libcalls symbols defined in an excluded archive. This may override
// versionId set by scanVersionScript(). // versionId set by scanVersionScript().

View File

@ -0,0 +1,37 @@
# REQUIRES: aarch64
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=aarch64 -o deplibs.o deplibs.s
# RUN: llvm-mc -filetype=obj -triple=aarch64 -o foo.o foo.s
# RUN: llvm-as -o lto.o lto.ll
# RUN: llvm-ar rc libdeplibs.a deplibs.o
# RUN: llvm-ar rc libfoo.a foo.o
## LTO emits a libcall (__aarch64_ldadd4_relax) that is resolved using a
## library (libdeplibs.a) that contains a .deplibs section pointing to a file
## (libfoo.a) not yet added to the link.
# RUN: not ld.lld lto.o -u a -L. -ldeplibs 2>&1 | FileCheck %s
# CHECK: error: input file 'foo.o' added after LTO
## Including the file before LTO prevents the issue.
# RUN: ld.lld lto.o -u a -L. -ldeplibs -lfoo
#--- foo.s
.global foo
foo:
#--- deplibs.s
.global __aarch64_ldadd4_relax
__aarch64_ldadd4_relax:
b foo
.section ".deplibs","MS",@llvm_dependent_libraries,1
.asciz "foo"
#--- lto.ll
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64"
define void @a(i32* nocapture %0) #0 {
%2 = atomicrmw add i32* %0, i32 1 monotonic, align 4
ret void
}
attributes #0 = { "target-features"="+outline-atomics" }