[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
2fcaa00d1e).
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: https://github.com/llvm/llvm-project/pull/143831
This commit is contained in:
@@ -368,6 +368,7 @@ struct Config {
|
||||
bool writeAddends;
|
||||
bool zCombreloc;
|
||||
bool zCopyreloc;
|
||||
bool zDynamicUndefined;
|
||||
bool zForceBti;
|
||||
bool zForceIbt;
|
||||
bool zGlobal;
|
||||
|
||||
@@ -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 <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");
|
||||
|
||||
@@ -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 <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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
@@ -301,7 +300,6 @@ static void demoteSymbolsAndComputeIsPreemptible(Ctx &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if (maybePreemptible)
|
||||
sym->isPreemptible = (sym->isUndefined() || sym->isExported) &&
|
||||
computeIsPreemptible(ctx, *sym);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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':
|
||||
|
||||
Reference in New Issue
Block a user