diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -307,6 +307,10 @@ template void parse(); void fetch(); + // Checks if a symbol should be fetched to override a common definition. + // If the symbol is either a strong or weak definition, we fetch. + bool shouldFetchForCommon(const StringRef &Name); + bool fetched = false; private: @@ -326,6 +330,10 @@ // more than once.) void fetch(const Archive::Symbol &sym); + // Checks if a symbol should be fetched to override a common definition. + // If the symbol is either a strong or weak definition, we fetch. + bool shouldFetchForCommon(const Archive::Symbol &sym); + size_t getMemberCount() const; size_t getFetchedMemberCount() const { return seen.size(); } diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1193,6 +1193,7 @@ file(std::move(file)) {} void ArchiveFile::parse() { + for (const Archive::Symbol &sym : file->symbols()) symtab->addSymbol(LazyArchive{*this, sym}); @@ -1225,6 +1226,67 @@ parseFile(file); } +bool isBitcodeUncommonDef(MemoryBufferRef mb, StringRef symName, + StringRef archiveName) { + IRSymtabFile symtabFile = check(readIRSymtab(mb)); + for (unsigned moduleIndex = 0; moduleIndex != symtabFile.Mods.size(); + ++moduleIndex) { + for (const irsymtab::Reader::SymbolRef &sym : + symtabFile.TheReader.module_symbols(moduleIndex)) { + if (sym.getName().equals(symName)) + return sym.isGlobal() && !sym.isUndefined() && !sym.isCommon(); + } + } + return false; +} + +template +bool isUncommonDef(MemoryBufferRef mb, StringRef symName, + StringRef archiveName) { + ObjFile *obj = make>(mb, archiveName); + StringRef stringtable = obj->getStringTable(); + + for (auto sym : obj->template getGlobalELFSyms()) { + Expected name = sym.getName(stringtable); + if (name && name.get().equals(symName)) + return sym.isDefined() && !sym.isCommon(); + } + return false; +} + +bool isUncommonDef(MemoryBufferRef mb, StringRef symName, + StringRef archiveName) { + switch (getELFKind(mb, archiveName)) { + case ELF32LEKind: + return isUncommonDef(mb, symName, archiveName); + case ELF32BEKind: + return isUncommonDef(mb, symName, archiveName); + case ELF64LEKind: + return isUncommonDef(mb, symName, archiveName); + case ELF64BEKind: + return isUncommonDef(mb, symName, archiveName); + default: + llvm_unreachable("getELFKind"); + } +} + +bool ArchiveFile::shouldFetchForCommon(const Archive::Symbol &sym) { + Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member for symbol " + + toELFString(sym)); + MemoryBufferRef mb = + CHECK(c.getMemoryBufferRef(), + toString(this) + + ": could not get the buffer for the member defining symbol " + + toELFString(sym)); + + if (isBitcode(mb)) + return isBitcodeUncommonDef(mb, sym.getName(), getName()); + + return isUncommonDef(mb, sym.getName(), getName()); +} + size_t ArchiveFile::getMemberCount() const { size_t count = 0; Error err = Error::success(); @@ -1745,6 +1807,13 @@ } } +bool LazyObjFile::shouldFetchForCommon(const StringRef &Name) { + if (isBitcode(mb)) + return isBitcodeUncommonDef(mb, Name, archiveName); + + return isUncommonDef(mb, Name, archiveName); +} + std::string elf::replaceThinLTOSuffix(StringRef path) { StringRef suffix = config->thinLTOObjectSuffixReplace.first; StringRef repl = config->thinLTOObjectSuffixReplace.second; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -690,7 +690,33 @@ other.value); } +template void replaceCommon(Symbol &oldSym, const LazyT &newSym) { + backwardReferences.erase(&oldSym); + oldSym.replace(newSym); + newSym.fetch(); +} + template void Symbol::resolveLazy(const LazyT &other) { + // For common objects, we want to look for strong or weak definitions that + // should be fetched as the cannonical definition instead. + if (isCommon() && isObject()) { + if (auto *laSym = dyn_cast(&other)) { + ArchiveFile *archive = cast(laSym->file); + const Archive::Symbol &archiveSym = laSym->sym; + if (archive->shouldFetchForCommon(archiveSym)) { + replaceCommon(*this, other); + return; + } + } + if (auto *loSym = dyn_cast(&other)) { + LazyObjFile *obj = dyn_cast(loSym->file); + if (obj->shouldFetchForCommon(loSym->getName())) { + replaceCommon(*this, other); + return; + } + } + } + if (!isUndefined()) { // See the comment in resolveUndefined(). if (isDefined()) diff --git a/lld/test/ELF/Inputs/blockdata.ll b/lld/test/ELF/Inputs/blockdata.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/blockdata.ll @@ -0,0 +1,4 @@ +target datalayout = "e-m:e-i64:64-n32:64" +target triple = "powerpc64le-unknown-linux-gnu" + +@block = dso_local local_unnamed_addr global <{ double, double, double, double, double, [507 x double] }> <{ double 0.000000e+00, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00, double 0x4005BF0A8B145703, [507 x double] zeroinitializer }>, align 8 diff --git a/lld/test/ELF/Inputs/commonblock.ll b/lld/test/ELF/Inputs/commonblock.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/commonblock.ll @@ -0,0 +1,25 @@ +target datalayout = "e-m:e-i64:64-n32:64" +target triple = "powerpc64le-unknown-linux-gnu" + +@block = common dso_local local_unnamed_addr global [512 x double] zeroinitializer, align 8 + +define dso_local double @foo(i32 signext %i) local_unnamed_addr { +entry: + %0 = load double, double* getelementptr inbounds ([512 x double], [512 x double]* @block, i64 0, i64 0), align 8 + ret double %0 +} + +define dso_local double @baz(i32 signext %i) local_unnamed_addr { +entry: + %0 = load double, double* getelementptr inbounds ([512 x double], [512 x double]* @block, i64 0, i64 0), align 8 + ret double %0 +} + +define dso_local double @bar(i32 signext %i) local_unnamed_addr { +entry: + %idxprom = sext i32 %i to i64 + %arrayidx = getelementptr inbounds [512 x double], [512 x double]* @block, i64 0, i64 %idxprom + %0 = load double, double* %arrayidx, align 8 + ret double %0 +} + diff --git a/lld/test/ELF/common-archive-lookup.s b/lld/test/ELF/common-archive-lookup.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/common-archive-lookup.s @@ -0,0 +1,216 @@ +# REQUIRES: ppc + +# Object files. +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym FOO=1 --defsym COMMON=1 %s -o %t1.o +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym BAR=1 --defsym COMMON=1 %s -o %t2.o +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym BAZ=1 --defsym COMMON=1 %s -o %t3.o +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym BARANDDATA=1 %s -o %t4.o +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym DATA=1 %s -o %tstrong_data_only.o +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym WEAK=1 %s -o %tweak_data_only.o + +# RUN: llvm-mc -triple=powerpc64le -filetype=obj --defsym TEST=1 %s -o %tmain.o + +# Object file archives. +# RUN: llvm-ar crs %t1.a %t1.o %t2.o %t3.o %tstrong_data_only.o +# RUN: llvm-ar crs %t2.a %t1.o %t2.o %t3.o %tweak_data_only.o +# RUN: llvm-ar crs %t3.a %t1.o %t3.o %t4.o %tstrong_data_only.o + +# Bitcode files. +# RUN: llvm-as -o %t1.bc %S/Inputs/commonblock.ll +# RUN: llvm-as -o %t2.bc %S/Inputs/blockdata.ll + +# Bitcode archive. +# RUN: llvm-ar crs %t4.a %t1.bc %t2.bc + +# RUN: ld.lld -o %t1 %tmain.o %t1.a +# RUN: llvm-objdump -D -j .data %t1 | FileCheck --check-prefix=TEST1 %s + +# RUN: ld.lld -o %t2 %tmain.o --start-lib %t1.o %t2.o %t3.o %tstrong_data_only.o --end-lib +# RUN: llvm-objdump -D -j .data %t2 | FileCheck --check-prefix=TEST1 %s + +# RUN: ld.lld -o %t3 %tmain.o %t2.a +# RUN: llvm-objdump -D -j .data %t3 | FileCheck --check-prefix=TEST1 %s + +# RUN: ld.lld -o %t4 %tmain.o --start-lib %t1.o %t2.o %t3.o %tweak_data_only.o --end-lib +# RUN: llvm-objdump -D -j .data %t4 | FileCheck --check-prefix=TEST1 %s + +# RUN: ld.lld -o %t5 %tmain.o %t3.a --print-map | FileCheck --check-prefix=MAP %s + +# RUN: ld.lld -o %t6 %tmain.o %t4.o %t1.a +# RUN: llvm-objdump -D -j .data %t6 | FileCheck --check-prefix=TEST2 %s + +# RUN: ld.lld -o %t7 %tmain.o %t4.o --start-lib %t1.o %t2.o %t3.o %tstrong_data_only.o --end-lib +# RUN: llvm-objdump -D -j .data %t7 | FileCheck --check-prefix=TEST2 %s + +# RUN: not ld.lld -o %t8 %tmain.o %t1.a %tstrong_data_only.o 2>&1 | \ +# RUN: FileCheck --check-prefix=ERR %s + +# RUN: not ld.lld -o %t9 %tmain.o --start-lib %t1.o %t2.o %t3.o %t4.o --end-lib %tstrong_data_only.o 2>&1 | \ +# RUN: FileCheck --check-prefix=ERR %s + +# ERR: ld.lld: error: duplicate symbol: block + +# RUN: ld.lld -o - %tmain.o %t4.a --lto-emit-asm | FileCheck --check-prefix=ASM %s + +# RUN: ld.lld -o - %tmain.o --start-lib %t1.bc %t2.bc --end-lib --lto-emit-asm | \ +# RUN: FileCheck --check-prefix=ASM %s + +# Function definitions which reference 'block'. +.ifdef FOO + .text + .abiversion 2 + .global foo + .type foo,@function +foo: +.Lgep_foo: + addis 2, 12, .TOC.-.Lgep_foo@ha + addi 2, 2, .TOC.-.Lgep_foo@l + .localentry foo, .-.Lgep_foo + + addis 3, 2, block@toc@ha + addi 3, 3, block@toc@l + blr +.endif + +.ifdef BAR + .text + .abiversion 2 + .global bar + .type bar,@function +bar: +.Lgep_bar: + addis 2, 12, .TOC.-.Lgep_bar@ha + addi 2, 2, .TOC.-.Lgep_bar@l + .localentry bar, .-.Lgep_bar + + addis 4, 2, block@toc@ha + addi 4, 4, block@toc@l + sldi 3, 3, 3 + lfdx 1, 4, 3 + blr +.endif + +.ifdef BAZ + .text + .abiversion 2 + .global baz + .type baz,@function +baz: +.Lgep_baz: + addis 2, 12, .TOC.-.Lgep_baz@ha + addi 2, 2, .TOC.-.Lgep_baz@l + .localentry baz, .-.Lgep_baz + + addis 3, 2, block@toc@ha + addi 3, 3, block@toc@l + blr +.endif + +# An alternate strong definition of block, in the same file as +# a different referenced symbol. +.ifdef BARANDDATA + .text + .abiversion 2 + .global bar + .type bar,@function +bar: +.Lgep_bar: + addis 2, 12, .TOC.-.Lgep_bar@ha + addi 2, 2, .TOC.-.Lgep_bar@l + .localentry bar, .-.Lgep_bar + + addis 4, 2, block@toc@ha + addi 4, 4, block@toc@l + sldi 3, 3, 3 + lfdx 1, 4, 3 + blr + + .data + .type block,@object + .global block + .p2align 3 +block: + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x4005bf0a8b145703 # double 2.7182818284589998 + .space 4056 + .size d, 4096 +.endif + +# Tentative definition of 'block'. +.ifdef COMMON + .type block,@object + .comm block,4096,8 +.endif + +# Strong definition of 'block'. +.ifdef DATA + .data + .type block,@object + .global block + .p2align 3 +block: + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x400921fb54442eea # double 3.1415926535900001 + .space 4056 + .size d, 4096 +.endif + +# Weak definition of `block`. +.ifdef WEAK + .data + .type block,@object + .weak block + .p2align 3 +block: + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x0000000000000000 + .quad 0x400921fb54442eea # double 3.1415926535900001 + .space 4056 + .size d, 4096 +.endif + +.ifdef TEST + .global _start +_start: + li 3, 5 + bl bar + blr +.endif + +# Ensure we have used the initialized definition of 'block' instead of a common definition. +# TEST1-LABEL: Disassembly of section .data: +# TEST1: : +# TEST1-NEXT: ... +# TEST1-NEXT: ea 2e 44 54 +# TEST1-NEXT: fb 21 09 40 +# TEST1-NEXT: ... + +# Expecting the strong definition from the object file, and the defintions from +# the archive do not interfere. +# TEST2-LABEL: Disassembly of section .data: +# TEST2: : +# TEST2-NEXT: ... +# TEST2-NEXT: 03 57 14 8b +# TEST2-NEXT: 0a bf 05 40 +# TEST2-NEXT: ... + +# MAP: 1000 8 {{.*}}tmp3.a(common-archive-lookup.s.tmp4.o):(.data) +# MAP-NEXT: 0 1 block + +# ASM: .type block,@object +# ASM: block: +# ASM-NEXT: .quad 0x0000000000000000 +# ASM-NEXT: .quad 0x0000000000000000 +# ASM-NEXT: .quad 0x0000000000000000 +# ASM-NEXT: .quad 0x0000000000000000 +# ASM-NEXT: .quad 0x4005bf0a8b145703 +# ASM-NEXT: .space 4056 +# ASM-NEXT: .size block, 4096