Skip to content

[ELF] Implement -z dynamic-undefined-weak #143831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ struct Config {
bool writeAddends;
bool zCombreloc;
bool zCopyreloc;
bool zDynamicUndefined;
bool zForceBti;
bool zForceIbt;
bool zGlobal;
Expand Down
8 changes: 8 additions & 0 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -3058,6 +3059,13 @@ template <class ELFT> 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");
Expand Down
14 changes: 8 additions & 6 deletions lld/ELF/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -360,7 +363,6 @@ void elf::parseVersionAndComputeIsPreemptible(Ctx &ctx) {
// can contain versions in the form of <name>@<version>.
// 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);
Expand All @@ -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);
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ static void demoteDefined(Defined &sym, DenseMap<SectionBase *, size_t> &map) {
static void demoteSymbolsAndComputeIsPreemptible(Ctx &ctx) {
llvm::TimeTraceScope timeScope("Demote symbols");
DenseMap<InputFile *, DenseMap<SectionBase *, size_t>> sectionIndexMap;
bool maybePreemptible = ctx.sharedFiles.size() || ctx.arg.shared;
for (Symbol *sym : ctx.symtab->getSymbols()) {
if (auto *d = dyn_cast<Defined>(sym)) {
if (d->section && !d->section->isLive())
Expand All @@ -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);
}
}

Expand Down
4 changes: 4 additions & 0 deletions lld/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/llvm/llvm-project/pull/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,
Expand Down
8 changes: 8 additions & 0 deletions lld/docs/ld.lld.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lld/test/ELF/driver.test
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check this is testing that the later -z nodynamic-undefined-weak overrides -z nodynamic-undefined-weak?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

# 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

Expand Down
6 changes: 6 additions & 0 deletions lld/test/ELF/weak-undef-got-plt.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions lld/test/ELF/weak-undef-hidden.s
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 16 additions & 3 deletions lld/test/ELF/weak-undef-rw.s
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down