diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 0a52dfe6901b..3a9001d2cc8b 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -368,6 +368,7 @@ struct Config { bool writeAddends; bool zCombreloc; bool zCopyreloc; + bool zDynamicUndefined; bool zForceBti; bool zForceIbt; bool zGlobal; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 12dac82c614a..87b19cf543d9 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -591,6 +591,7 @@ static void checkZOptions(Ctx &ctx, opt::InputArgList &args) { args::getZOptionValue(args, OPT_z, "max-page-size", 0); args::getZOptionValue(args, OPT_z, "common-page-size", 0); getZFlag(args, "rel", "rela", false); + getZFlag(args, "dynamic-undefined-weak", "nodynamic-undefined-weak", false); for (auto *arg : args.filtered(OPT_z)) if (!arg->isClaimed()) Warn(ctx) << "unknown -z value: " << StringRef(arg->getValue()); @@ -3058,6 +3059,13 @@ template void LinkerDriver::link(opt::InputArgList &args) { ctx.hasDynsym = !ctx.sharedFiles.empty() || ctx.arg.isPic; ctx.arg.exportDynamic &= ctx.hasDynsym; + // Preemptibility of undefined symbols when ctx.hasDynsym is true. Default is + // true for dynamic linking. + ctx.arg.zDynamicUndefined = + getZFlag(args, "dynamic-undefined-weak", "nodynamic-undefined-weak", + ctx.sharedFiles.size() || ctx.arg.shared) && + ctx.hasDynsym; + // If an entry symbol is in a static archive, pull out that file now. if (Symbol *sym = ctx.symtab->find(ctx.arg.entry)) handleUndefined(ctx, sym, "--entry"); diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index c461dfed0d74..de839795c50d 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -333,10 +333,13 @@ bool elf::computeIsPreemptible(Ctx &ctx, const Symbol &sym) { if (sym.visibility() != STV_DEFAULT) return false; - // At this point copy relocations have not been created yet, so any - // symbol that is not defined locally is preemptible. + // At this point copy relocations have not been created yet. + // Shared symbols are preemptible. Undefined symbols are preemptible + // when zDynamicUndefined (default in dynamic linking). Weakness is not + // checked, though undefined non-weak would typically trigger relocation + // errors unless options like -z undefs are used. if (!sym.isDefined()) - return true; + return !sym.isUndefined() || ctx.arg.zDynamicUndefined; if (!ctx.arg.shared) return false; @@ -360,7 +363,6 @@ void elf::parseVersionAndComputeIsPreemptible(Ctx &ctx) { // can contain versions in the form of @. // Let them parse and update their names to exclude version suffix. // In addition, compute isExported and isPreemptible. - bool maybePreemptible = ctx.sharedFiles.size() || ctx.arg.shared; for (Symbol *sym : ctx.symtab->getSymbols()) { if (sym->hasVersionSuffix) sym->parseSymbolVersion(ctx); @@ -369,11 +371,11 @@ void elf::parseVersionAndComputeIsPreemptible(Ctx &ctx) { continue; } if (!sym->isDefined() && !sym->isCommon()) { - sym->isPreemptible = maybePreemptible && computeIsPreemptible(ctx, *sym); + sym->isPreemptible = computeIsPreemptible(ctx, *sym); } else if (ctx.arg.exportDynamic && (sym->isUsedInRegularObj || !sym->ltoCanOmit)) { sym->isExported = true; - sym->isPreemptible = maybePreemptible && computeIsPreemptible(ctx, *sym); + sym->isPreemptible = computeIsPreemptible(ctx, *sym); } } } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 3d9888f576f0..15909daf51ab 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -285,7 +285,6 @@ static void demoteDefined(Defined &sym, DenseMap &map) { static void demoteSymbolsAndComputeIsPreemptible(Ctx &ctx) { llvm::TimeTraceScope timeScope("Demote symbols"); DenseMap> sectionIndexMap; - bool maybePreemptible = ctx.sharedFiles.size() || ctx.arg.shared; for (Symbol *sym : ctx.symtab->getSymbols()) { if (auto *d = dyn_cast(sym)) { if (d->section && !d->section->isLive()) @@ -301,9 +300,8 @@ static void demoteSymbolsAndComputeIsPreemptible(Ctx &ctx) { } } - if (maybePreemptible) - sym->isPreemptible = (sym->isUndefined() || sym->isExported) && - computeIsPreemptible(ctx, *sym); + sym->isPreemptible = (sym->isUndefined() || sym->isExported) && + computeIsPreemptible(ctx, *sym); } } diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index 5c180fd8fbee..064ed0828c31 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -25,6 +25,10 @@ Non-comprehensive list of changes in this release ELF Improvements ---------------- +* Added ``-z dynamic-undefined-weak`` to make undefined weak symbols dynamic + when the dynamic symbol table is present. + (`#143831 `_) + * For AArch64, added support for ``-zgcs-report-dynamic``, enabling checks for GNU GCS Attribute Flags in Dynamic Objects when GCS is enabled. Inherits value from ``-zgcs-report`` (capped at ``warning`` level) unless user-defined, diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 57aa2be5907b..cfacdb081a80 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -793,6 +793,14 @@ Specify how to report the missing GNU_PROPERTY_X86_FEATURE_1_IBT or GNU_PROPERTY .Cm none is the default, linker will not report the missing property otherwise will be reported as a warning or an error. .Pp +.It Cm dynamic-undefined-weak +Make undefined weak symbols dynamic when the dynamic symbol table is present, if they are referenced from +relocatable object files and not forced local by symbol visibility or versioning. Do not make them dynamic when +.Cm nodynamic-undefined-weak +is specified. +.Cm dynamic-undefined-weak +is the default when building a shared object, or when an input shared object is present. +.Pp .It Cm pauth-report Ns = Ns Ar [none|warning|error] Specify how to report the missing GNU_PROPERTY_AARCH64_FEATURE_PAUTH property. .Cm none diff --git a/lld/test/ELF/driver.test b/lld/test/ELF/driver.test index 45d73607c8ac..6d5761212cc3 100644 --- a/lld/test/ELF/driver.test +++ b/lld/test/ELF/driver.test @@ -47,7 +47,8 @@ # ERR9: error: cannot open output file utput=/no/such/file # RUN: ld.lld %t -z foo -o /dev/null 2>&1 | FileCheck -check-prefix=ERR10 %s --implicit-check-not=warning: -# RUN: ld.lld %t -z foo -z rel -z rela -z max-page-size=1 -z common-page-size=1 -o /dev/null --version 2>&1 | \ +# RUN: ld.lld %t -z foo -z rel -z rela -z max-page-size=1 -z common-page-size=1 -z dynamic-undefined-weak \ +# RUN: -z nodynamic-undefined-weak -o /dev/null --version 2>&1 | \ # RUN: FileCheck -check-prefix=ERR10 %s --implicit-check-not=warning: # ERR10: warning: unknown -z value: foo diff --git a/lld/test/ELF/weak-undef-got-plt.s b/lld/test/ELF/weak-undef-got-plt.s index 0ee3da2cd3b4..48a7914e5b98 100644 --- a/lld/test/ELF/weak-undef-got-plt.s +++ b/lld/test/ELF/weak-undef-got-plt.s @@ -6,11 +6,17 @@ # RUN: ld.lld a.o -o a # RUN: llvm-readelf -r a | FileCheck %s --check-prefix=NORELOC +# RUN: ld.lld a.o -o a -z dynamic-undefined-weak +# RUN: llvm-readelf -r a | FileCheck %s --check-prefix=NORELOC # RUN: ld.lld a.o s.so -o as # RUN: llvm-objdump -dR as | FileCheck %s +# RUN: ld.lld a.o s.so -o as -z nodynamic-undefined-weak +# RUN: llvm-readelf -r a | FileCheck %s --check-prefix=NORELOC # RUN: ld.lld -pie a.o s.so -o as.pie # RUN: llvm-objdump -dR as.pie | FileCheck %s +# RUN: ld.lld -pie a.o s.so -o as.pie -z nodynamic-undefined-weak +# RUN: llvm-readelf -r as.pie | FileCheck --check-prefix=NORELOC %s # RUN: ld.lld -shared a.o -o a.so # RUN: llvm-objdump -dR a.so | FileCheck %s diff --git a/lld/test/ELF/weak-undef-hidden.s b/lld/test/ELF/weak-undef-hidden.s index 2baad5738c36..ad2ba29ec27a 100644 --- a/lld/test/ELF/weak-undef-hidden.s +++ b/lld/test/ELF/weak-undef-hidden.s @@ -5,6 +5,10 @@ // RUN: ld.lld %t.o -o %t -pie // RUN: llvm-readobj -r -S --section-data %t | FileCheck %s +/// -z dynamic-undefined-weak does not affect hidden undefined symbols. +// RUN: ld.lld %t.o -o %t.so -shared -z dynamic-undefined-weak +// RUN: llvm-readobj -r -S --section-data %t.so | FileCheck %s + /// This is usually guarded with a comparison. Don't report an error. call g diff --git a/lld/test/ELF/weak-undef-rw.s b/lld/test/ELF/weak-undef-rw.s index 497228a3cf90..8d777669b7e1 100644 --- a/lld/test/ELF/weak-undef-rw.s +++ b/lld/test/ELF/weak-undef-rw.s @@ -18,9 +18,22 @@ ## gABI leaves the behavior of weak undefined references implementation defined. ## We choose to resolve them statically for static linking and produce dynamic relocations ## for dynamic linking (-shared or at least one input DSO). -## -## Note: Some ports of GNU ld support -z nodynamic-undefined-weak that we don't -## implement. + +## -z dynamic-undefined-weak is ignored if .dynsym is absent (-no-pie without DSO) +# RUN: ld.lld a.o -o a.d -z dynamic-undefined-weak 2>&1 | count 0 +# RUN: llvm-readelf -r --hex-dump=.data a.d | FileCheck %s --check-prefix=STATIC + +## Currently no effect for S+A relocations. +# RUN: ld.lld a.o s.so -o as.d -z dynamic-undefined-weak +# RUN: llvm-readelf -r --hex-dump=.data as.d | FileCheck %s --check-prefix=STATIC + +## -z dynamic-undefined-weak forces dynamic relocations if .dynsym is present. +# RUN: ld.lld a.o -o a.pie.d -pie -z dynamic-undefined-weak +# RUN: llvm-readelf -r a.pie.d | FileCheck %s --check-prefix=DYN + +## -z nodynamic-undefined-weak suppresses dynamic relocations. +# RUN: ld.lld a.o -o a.so.n -shared -z dynamic-undefined-weak -z nodynamic-undefined-weak +# RUN: llvm-readelf -r --hex-dump=.data a.so.n | FileCheck %s --check-prefix=STATIC # STATIC: no relocations # STATIC: Hex dump of section '.data':