diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 56229334f9a44..1af62c30faaac 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -331,6 +331,7 @@ struct Config { StripPolicy strip; UnresolvedPolicy unresolvedSymbols; UnresolvedPolicy unresolvedSymbolsInShlib; + bool allowNonExportedSymbolsSharedWithDso = true; Target2Policy target2; bool power10Stubs; ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 5f88389a58408..58c2d70fdee2b 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1215,6 +1215,9 @@ static void readConfigs(opt::InputArgList &args) { errorHandler().vsDiagnostics = args.hasArg(OPT_visual_studio_diagnostics_format, false); + config->allowNonExportedSymbolsSharedWithDso = + args.hasFlag(OPT_allow_non_exported_symbols_shared_with_dso, + OPT_no_allow_non_exported_symbols_shared_with_dso, true); config->allowMultipleDefinition = args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index a0d4be8ff9885..86e91b88d3aa8 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1521,6 +1521,7 @@ template void SharedFile::parse() { Symbol *s = symtab.addSymbol( Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); s->exportDynamic = true; + s->setFlags(HAS_SHARED_REF); if (s->isUndefined() && sym.getBinding() != STB_WEAK && config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore) requiredSymbols.push_back(s); @@ -1545,6 +1546,7 @@ template void SharedFile::parse() { auto *s = symtab.addSymbol( SharedSymbol{*this, name, sym.getBinding(), sym.st_other, sym.getType(), sym.st_value, sym.st_size, alignment}); + s->setFlags(HAS_SHARED_DEF); if (s->file == this) s->verdefIndex = ver; } @@ -1562,6 +1564,7 @@ template void SharedFile::parse() { auto *s = symtab.addSymbol( SharedSymbol{*this, saver().save(name), sym.getBinding(), sym.st_other, sym.getType(), sym.st_value, sym.st_size, alignment}); + s->setFlags(HAS_SHARED_DEF); if (s->file == this) s->verdefIndex = idx; } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 9a23f48350644..e791fb9b5d643 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -105,6 +105,10 @@ defm Ttext: Eq<"Ttext", "Same as --section-start with .text as the sectionname"> def Ttext_segment: Separate<["-", "--"], "Ttext-segment">; +defm allow_non_exported_symbols_shared_with_dso: BB<"allow-non-exported-symbols-shared-with-dso", + "Allow non-exported symbols that are also defined or referenced by a DSO (default)", + "Do not allow non-exported symbols that are also defined or referenced by a DSO">; + defm allow_multiple_definition: B<"allow-multiple-definition", "Allow multiple definitions", "Do not allow multiple definitions (default)">; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 4addb79d12579..02935ac53826c 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -54,6 +54,8 @@ enum { NEEDS_TLSGD_TO_IE = 1 << 6, NEEDS_GOT_DTPREL = 1 << 7, NEEDS_TLSIE = 1 << 8, + HAS_SHARED_DEF = 1 << 9, + HAS_SHARED_REF = 1 << 10, }; // Some index properties of a symbol are stored separately in this auxiliary diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index a84e4864ab0e5..45e570a6fa57d 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -272,8 +272,23 @@ static void demoteDefined(Defined &sym, DenseMap &map) { static void demoteSymbolsAndComputeIsPreemptible() { llvm::TimeTraceScope timeScope("Demote symbols"); DenseMap> sectionIndexMap; + bool allowNonExportedSymbolsSharedWithDso = + config->allowNonExportedSymbolsSharedWithDso; for (Symbol *sym : symtab.getSymbols()) { if (auto *d = dyn_cast(sym)) { + if (!allowNonExportedSymbolsSharedWithDso && + d->visibility() != ELF::STV_DEFAULT && + d->visibility() != ELF::STV_PROTECTED) { + auto flags = d->flags.load(std::memory_order_relaxed); + if (flags & HAS_SHARED_DEF) { + errorOrWarn("non-exported symbol also defined by DSO: " + toString(*sym) + + "\n>>> defined by " + toString(d->file)); + } else if (flags & HAS_SHARED_REF) { + errorOrWarn("non-exported symbol also referenced by DSO: " + + toString(*sym) + "\n>>> defined by " + toString(d->file)); + } + } + if (d->section && !d->section->isLive()) demoteDefined(*d, sectionIndexMap[d->file]); } else { @@ -2016,6 +2031,11 @@ template void Writer::finalizeSections() { // to catch more cases. That is too much for us. Our approach resembles // the one used in ld.gold, achieves a good balance to be useful but not // too smart. + // + // If a DSO reference is resolved by a SharedSymbol, but the SharedSymbol + // is overridden by a hidden visibility Defined (which is later discarded + // due to GC), don't report the diagnostic. However, this may indicate an + // unintended SharedSymbol. for (SharedFile *file : ctx.sharedFiles) { bool allNeededIsKnown = llvm::all_of(file->dtNeeded, [&](StringRef needed) { @@ -2024,6 +2044,8 @@ template void Writer::finalizeSections() { if (!allNeededIsKnown) continue; for (Symbol *sym : file->requiredSymbols) { + if (sym->hasFlag(HAS_SHARED_DEF)) + continue; if (sym->isUndefined() && !sym->isWeak()) { diagnose("undefined reference due to --no-allow-shlib-undefined: " + toString(*sym) + "\n>>> referenced by " + toString(file)); diff --git a/lld/test/ELF/allow-non-exported-symbols-shared-with-dso.s b/lld/test/ELF/allow-non-exported-symbols-shared-with-dso.s new file mode 100644 index 0000000000000..1a624ee750595 --- /dev/null +++ b/lld/test/ELF/allow-non-exported-symbols-shared-with-dso.s @@ -0,0 +1,44 @@ +# REQUIRES: x86 +# RUN: rm -rf %t && split-file %s %t && cd %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 weak.s -o weak.o + +# RUN: llvm-mc -filetype=obj -triple=x86_64 def.s -o def.o +# RUN: ld.lld -shared -soname=def.so def.o -o def.so +# RUN: ld.lld a.o def.so -o /dev/null +# RUN: ld.lld a.o def.so -o /dev/null --no-allow-non-exported-symbols-shared-with-dso --allow-non-exported-symbols-shared-with-dso + +# RUN: not ld.lld a.o def.so --no-allow-non-exported-symbols-shared-with-dso 2>&1 | FileCheck %s --check-prefix=CHECK1 +# RUN: not ld.lld --gc-sections weak.o def.so a.o --no-allow-non-exported-symbols-shared-with-dso 2>&1 | FileCheck %s --check-prefix=CHECK1 + +# CHECK1: error: non-exported symbol also defined by DSO: foo +# CHECK1-NEXT: >>> defined by {{.*}}a.o + +# RUN: llvm-mc -filetype=obj -triple=x86_64 ref.s -o ref.o +# RUN: ld.lld -shared -soname=ref.so ref.o -o ref.so +# RUN: not ld.lld a.o ref.so --no-allow-non-exported-symbols-shared-with-dso 2>&1 | FileCheck %s --check-prefix=CHECK2 + +# CHECK2: error: non-exported symbol also referenced by DSO: foo +# CHECK2-NEXT: >>> defined by {{.*}}a.o + +## See also allow-shlib-undefined.s. + +#--- a.s +.globl _start, foo +_start: + +## The check kicks in even if .text.foo is discarded. +.section .text.foo,"ax" +.hidden foo +foo: + +#--- weak.s +.weak foo +foo: + +#--- def.s +.globl foo +foo: + +#--- ref.s +call foo diff --git a/lld/test/ELF/allow-shlib-undefined.s b/lld/test/ELF/allow-shlib-undefined.s index 56b44e144661c..efc3813b189f2 100644 --- a/lld/test/ELF/allow-shlib-undefined.s +++ b/lld/test/ELF/allow-shlib-undefined.s @@ -40,7 +40,8 @@ # RUN: not ld.lld --gc-sections main.o a.so def-hidden.o -o /dev/null 2>&1 | FileCheck %s ## The definition def.so is ignored. # RUN: ld.lld -shared def.o -o def.so -# RUN: not ld.lld --gc-sections main.o a.so def.so def-hidden.o -o /dev/null 2>&1 | FileCheck %s +# RUN: ld.lld --gc-sections main.o a.so def.so def-hidden.o --fatal-warnings -o /dev/null +# RUN: not ld.lld --gc-sections main.o a.so def.so def-hidden.o -o /dev/null --no-allow-non-exported-symbols-shared-with-dso 2>&1 | FileCheck %s --check-prefix=HIDDEN # CHECK-NOT: error: # CHECK: error: undefined reference due to --no-allow-shlib-undefined: x1{{$}} @@ -61,6 +62,11 @@ # NONEXPORTED: error: non-exported symbol 'x1' in 'def-hidden.o' is referenced by DSO 'a.so' # NONEXPORTED-NOT: {{.}} +# HIDDEN-NOT: error: +# HIDDEN: error: non-exported symbol also defined by DSO: x1 +# HIDDEN-NEXT: >>> defined by def-hidden.o +# HIDDEN-NOT: {{.}} + #--- main.s .globl _start _start: