Skip to content

Commit 07dad4e

Browse files
authored
[ELF] Implement -z dynamic-undefined-weak
The behavior of an undefined weak reference is implementation defined. For static -no-pie linking, dynamic relocations are generally avoided (except IRELATIVE). -shared linking generally emits dynamic relocations. Dynamic -no-pie linking and -pie allow flexibility. Changes adjust the behavior for better consistency and simpler internal representation, e.g. https://reviews.llvm.org/D63003 https://reviews.llvm.org/D105164 (generalized to undefined non-weak in 2fcaa00). GNU ld introduced -z [no]dynamic-undefined-weak option to fine-tune the behavior. (The option is not very effective with -no-pie, e.g. on x86-64, `ld.bfd a.o s.so -z dynamic-undefined-weak` generates R_X86_64_NONE relocations instead of GLOB_DAT/JUMP_SLOT) This patch implements -z [no]dynamic-undefined-weak option. The effects are summarized as follows: * Static -no-pie: no-op * Dynamic -no-pie: nodynamic-undefined-weak suppresses GLOB_DAT/JUMP_SLOT * Static -pie: dynamic-undefined-weak generates ABS/GLOB_DAT/JUMP_SLOT. https://discourse.llvm.org/t/lld-weak-undefined-symbols-in-vdso-only/86749 * Dynamic -pie: nodynamic-undefined-weak suppresses ABS/GLOB_DAT/JUMP_SLOT The -pie behavior likely stays stable while -no-pie (`!ctx.arg.isPic` in `isStaticLinkTimeConstant`) behavior will likely change in the future. The current default value of ctx.arg.zDynamicUndefined is selected to prevent behavior changes. Pull Request: #143831
1 parent 7232c07 commit 07dad4e

File tree

10 files changed

+59
-14
lines changed

10 files changed

+59
-14
lines changed

lld/ELF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ struct Config {
368368
bool writeAddends;
369369
bool zCombreloc;
370370
bool zCopyreloc;
371+
bool zDynamicUndefined;
371372
bool zForceBti;
372373
bool zForceIbt;
373374
bool zGlobal;

lld/ELF/Driver.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ static void checkZOptions(Ctx &ctx, opt::InputArgList &args) {
591591
args::getZOptionValue(args, OPT_z, "max-page-size", 0);
592592
args::getZOptionValue(args, OPT_z, "common-page-size", 0);
593593
getZFlag(args, "rel", "rela", false);
594+
getZFlag(args, "dynamic-undefined-weak", "nodynamic-undefined-weak", false);
594595
for (auto *arg : args.filtered(OPT_z))
595596
if (!arg->isClaimed())
596597
Warn(ctx) << "unknown -z value: " << StringRef(arg->getValue());
@@ -3058,6 +3059,13 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
30583059
ctx.hasDynsym = !ctx.sharedFiles.empty() || ctx.arg.isPic;
30593060
ctx.arg.exportDynamic &= ctx.hasDynsym;
30603061

3062+
// Preemptibility of undefined symbols when ctx.hasDynsym is true. Default is
3063+
// true for dynamic linking.
3064+
ctx.arg.zDynamicUndefined =
3065+
getZFlag(args, "dynamic-undefined-weak", "nodynamic-undefined-weak",
3066+
ctx.sharedFiles.size() || ctx.arg.shared) &&
3067+
ctx.hasDynsym;
3068+
30613069
// If an entry symbol is in a static archive, pull out that file now.
30623070
if (Symbol *sym = ctx.symtab->find(ctx.arg.entry))
30633071
handleUndefined(ctx, sym, "--entry");

lld/ELF/Symbols.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,13 @@ bool elf::computeIsPreemptible(Ctx &ctx, const Symbol &sym) {
333333
if (sym.visibility() != STV_DEFAULT)
334334
return false;
335335

336-
// At this point copy relocations have not been created yet, so any
337-
// symbol that is not defined locally is preemptible.
336+
// At this point copy relocations have not been created yet.
337+
// Shared symbols are preemptible. Undefined symbols are preemptible
338+
// when zDynamicUndefined (default in dynamic linking). Weakness is not
339+
// checked, though undefined non-weak would typically trigger relocation
340+
// errors unless options like -z undefs are used.
338341
if (!sym.isDefined())
339-
return true;
342+
return !sym.isUndefined() || ctx.arg.zDynamicUndefined;
340343

341344
if (!ctx.arg.shared)
342345
return false;
@@ -360,7 +363,6 @@ void elf::parseVersionAndComputeIsPreemptible(Ctx &ctx) {
360363
// can contain versions in the form of <name>@<version>.
361364
// Let them parse and update their names to exclude version suffix.
362365
// In addition, compute isExported and isPreemptible.
363-
bool maybePreemptible = ctx.sharedFiles.size() || ctx.arg.shared;
364366
for (Symbol *sym : ctx.symtab->getSymbols()) {
365367
if (sym->hasVersionSuffix)
366368
sym->parseSymbolVersion(ctx);
@@ -369,11 +371,11 @@ void elf::parseVersionAndComputeIsPreemptible(Ctx &ctx) {
369371
continue;
370372
}
371373
if (!sym->isDefined() && !sym->isCommon()) {
372-
sym->isPreemptible = maybePreemptible && computeIsPreemptible(ctx, *sym);
374+
sym->isPreemptible = computeIsPreemptible(ctx, *sym);
373375
} else if (ctx.arg.exportDynamic &&
374376
(sym->isUsedInRegularObj || !sym->ltoCanOmit)) {
375377
sym->isExported = true;
376-
sym->isPreemptible = maybePreemptible && computeIsPreemptible(ctx, *sym);
378+
sym->isPreemptible = computeIsPreemptible(ctx, *sym);
377379
}
378380
}
379381
}

lld/ELF/Writer.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ static void demoteDefined(Defined &sym, DenseMap<SectionBase *, size_t> &map) {
285285
static void demoteSymbolsAndComputeIsPreemptible(Ctx &ctx) {
286286
llvm::TimeTraceScope timeScope("Demote symbols");
287287
DenseMap<InputFile *, DenseMap<SectionBase *, size_t>> sectionIndexMap;
288-
bool maybePreemptible = ctx.sharedFiles.size() || ctx.arg.shared;
289288
for (Symbol *sym : ctx.symtab->getSymbols()) {
290289
if (auto *d = dyn_cast<Defined>(sym)) {
291290
if (d->section && !d->section->isLive())
@@ -301,9 +300,8 @@ static void demoteSymbolsAndComputeIsPreemptible(Ctx &ctx) {
301300
}
302301
}
303302

304-
if (maybePreemptible)
305-
sym->isPreemptible = (sym->isUndefined() || sym->isExported) &&
306-
computeIsPreemptible(ctx, *sym);
303+
sym->isPreemptible = (sym->isUndefined() || sym->isExported) &&
304+
computeIsPreemptible(ctx, *sym);
307305
}
308306
}
309307

lld/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ Non-comprehensive list of changes in this release
2525

2626
ELF Improvements
2727
----------------
28+
* Added ``-z dynamic-undefined-weak`` to make undefined weak symbols dynamic
29+
when the dynamic symbol table is present.
30+
(`#143831 <https://github.com/llvm/llvm-project/pull/143831>`_)
31+
2832
* For AArch64, added support for ``-zgcs-report-dynamic``, enabling checks for
2933
GNU GCS Attribute Flags in Dynamic Objects when GCS is enabled. Inherits value
3034
from ``-zgcs-report`` (capped at ``warning`` level) unless user-defined,

lld/docs/ld.lld.1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,14 @@ Specify how to report the missing GNU_PROPERTY_X86_FEATURE_1_IBT or GNU_PROPERTY
793793
.Cm none
794794
is the default, linker will not report the missing property otherwise will be reported as a warning or an error.
795795
.Pp
796+
.It Cm dynamic-undefined-weak
797+
Make undefined weak symbols dynamic when the dynamic symbol table is present, if they are referenced from
798+
relocatable object files and not forced local by symbol visibility or versioning. Do not make them dynamic when
799+
.Cm nodynamic-undefined-weak
800+
is specified.
801+
.Cm dynamic-undefined-weak
802+
is the default when building a shared object, or when an input shared object is present.
803+
.Pp
796804
.It Cm pauth-report Ns = Ns Ar [none|warning|error]
797805
Specify how to report the missing GNU_PROPERTY_AARCH64_FEATURE_PAUTH property.
798806
.Cm none

lld/test/ELF/driver.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
# ERR9: error: cannot open output file utput=/no/such/file
4848

4949
# RUN: ld.lld %t -z foo -o /dev/null 2>&1 | FileCheck -check-prefix=ERR10 %s --implicit-check-not=warning:
50-
# 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 | \
50+
# RUN: ld.lld %t -z foo -z rel -z rela -z max-page-size=1 -z common-page-size=1 -z dynamic-undefined-weak \
51+
# RUN: -z nodynamic-undefined-weak -o /dev/null --version 2>&1 | \
5152
# RUN: FileCheck -check-prefix=ERR10 %s --implicit-check-not=warning:
5253
# ERR10: warning: unknown -z value: foo
5354

lld/test/ELF/weak-undef-got-plt.s

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@
66

77
# RUN: ld.lld a.o -o a
88
# RUN: llvm-readelf -r a | FileCheck %s --check-prefix=NORELOC
9+
# RUN: ld.lld a.o -o a -z dynamic-undefined-weak
10+
# RUN: llvm-readelf -r a | FileCheck %s --check-prefix=NORELOC
911
# RUN: ld.lld a.o s.so -o as
1012
# RUN: llvm-objdump -dR as | FileCheck %s
13+
# RUN: ld.lld a.o s.so -o as -z nodynamic-undefined-weak
14+
# RUN: llvm-readelf -r a | FileCheck %s --check-prefix=NORELOC
1115

1216
# RUN: ld.lld -pie a.o s.so -o as.pie
1317
# RUN: llvm-objdump -dR as.pie | FileCheck %s
18+
# RUN: ld.lld -pie a.o s.so -o as.pie -z nodynamic-undefined-weak
19+
# RUN: llvm-readelf -r as.pie | FileCheck --check-prefix=NORELOC %s
1420

1521
# RUN: ld.lld -shared a.o -o a.so
1622
# RUN: llvm-objdump -dR a.so | FileCheck %s

lld/test/ELF/weak-undef-hidden.s

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
// RUN: ld.lld %t.o -o %t -pie
66
// RUN: llvm-readobj -r -S --section-data %t | FileCheck %s
77

8+
/// -z dynamic-undefined-weak does not affect hidden undefined symbols.
9+
// RUN: ld.lld %t.o -o %t.so -shared -z dynamic-undefined-weak
10+
// RUN: llvm-readobj -r -S --section-data %t.so | FileCheck %s
11+
812
/// This is usually guarded with a comparison. Don't report an error.
913
call g
1014

lld/test/ELF/weak-undef-rw.s

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,22 @@
1818
## gABI leaves the behavior of weak undefined references implementation defined.
1919
## We choose to resolve them statically for static linking and produce dynamic relocations
2020
## for dynamic linking (-shared or at least one input DSO).
21-
##
22-
## Note: Some ports of GNU ld support -z nodynamic-undefined-weak that we don't
23-
## implement.
21+
22+
## -z dynamic-undefined-weak is ignored if .dynsym is absent (-no-pie without DSO)
23+
# RUN: ld.lld a.o -o a.d -z dynamic-undefined-weak 2>&1 | count 0
24+
# RUN: llvm-readelf -r --hex-dump=.data a.d | FileCheck %s --check-prefix=STATIC
25+
26+
## Currently no effect for S+A relocations.
27+
# RUN: ld.lld a.o s.so -o as.d -z dynamic-undefined-weak
28+
# RUN: llvm-readelf -r --hex-dump=.data as.d | FileCheck %s --check-prefix=STATIC
29+
30+
## -z dynamic-undefined-weak forces dynamic relocations if .dynsym is present.
31+
# RUN: ld.lld a.o -o a.pie.d -pie -z dynamic-undefined-weak
32+
# RUN: llvm-readelf -r a.pie.d | FileCheck %s --check-prefix=DYN
33+
34+
## -z nodynamic-undefined-weak suppresses dynamic relocations.
35+
# RUN: ld.lld a.o -o a.so.n -shared -z dynamic-undefined-weak -z nodynamic-undefined-weak
36+
# RUN: llvm-readelf -r --hex-dump=.data a.so.n | FileCheck %s --check-prefix=STATIC
2437

2538
# STATIC: no relocations
2639
# STATIC: Hex dump of section '.data':

0 commit comments

Comments
 (0)