-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[ELF] Suppress --no-allow-shlib-undefined diagnostic when a SharedSymbol is overridden by a hidden visibility Defined which is later discarded #70130
Conversation
@llvm/pr-subscribers-lld @llvm/pr-subscribers-lld-elf Author: Fangrui Song (MaskRay) ChangesCommit 1981b1b unexpectedly made --no-allow-shlib-undefined stronger:
When all the conditions are satisfied, the new --no-allow-shlib-undefined code Technically, the hidden Defined in the executable can be intentional: it can be Possible future extension: create a linker option to error about the cases I haven't yet identified an entirely legitimate use case of a hidden Defined Full diff: https://github.com/llvm/llvm-project/pull/70130.diff 4 Files Affected:
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index a0d4be8ff9885b0..c332470cfd57320 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -1545,6 +1545,7 @@ template <class ELFT> 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 +1563,7 @@ template <class ELFT> 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/Symbols.h b/lld/ELF/Symbols.h
index 4addb79d1257914..8c1be13dbc493d7 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -54,6 +54,7 @@ enum {
NEEDS_TLSGD_TO_IE = 1 << 6,
NEEDS_GOT_DTPREL = 1 << 7,
NEEDS_TLSIE = 1 << 8,
+ HAS_SHARED_DEF = 1 << 9,
};
// 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 57e1aa06c6aa873..a46a97755ea5f89 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -2016,6 +2016,11 @@ template <class ELFT> void Writer<ELFT>::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,7 +2029,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
if (!allNeededIsKnown)
continue;
for (Symbol *sym : file->requiredSymbols)
- if (sym->isUndefined() && !sym->isWeak())
+ if (sym->isUndefined() && !sym->isWeak() && !sym->hasFlag(HAS_SHARED_DEF))
diagnose("undefined reference due to --no-allow-shlib-undefined: " +
toString(*sym) + "\n>>> referenced by " + toString(file));
}
diff --git a/lld/test/ELF/allow-shlib-undefined.s b/lld/test/ELF/allow-shlib-undefined.s
index 03f047b02d75d52..16407b9645c8a07 100644
--- a/lld/test/ELF/allow-shlib-undefined.s
+++ b/lld/test/ELF/allow-shlib-undefined.s
@@ -46,7 +46,8 @@
# RUN: not ld.lld --gc-sections %t.o %t.so %tdef-hidden.o -o /dev/null 2>&1 | FileCheck %s
## The definition %tdef.so is ignored.
# RUN: ld.lld -shared -soname=tdef.so %tdef.o -o %tdef.so
-# RUN: not ld.lld --gc-sections %t.o %t.so %tdef.so %tdef-hidden.o -o /dev/null 2>&1 | FileCheck %s
+# RUN: ld.lld --gc-sections %t.o %t.so %tdef.so %tdef-hidden.o -o /dev/null --fatal-warnings
+# RUN: ld.lld --gc-sections %t.o %t.so %tdef-hidden.o %tdef.so -o /dev/null --fatal-warnings
.globl _start
_start:
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
See also the Discourse post Relocatable file definitions shared with DSO |
Not had a chance to look at the code yet. I've replied on the Discourse post, hopefully I understood it correctly. |
592cacc
to
a350e00
Compare
a350e00
to
5f6dd33
Compare
Commit 1981b1b unexpectedly strengthened --no-allow-shlib-undefined which will be relaxed by llvm#70130. This patch adds --no-allow-non-exported-symbols-shared-with-dso to restore the check and make the check catch more cases: * report errors in the presence of a DSO definition * make the check work whether or not --gc-sections discards the symbol Commit 1981b1b has caught several brittle build issues. For example, ``` libfdio.so: reference to _Znam libclang_rt.asan.so: shared definition of _Znam libc++.a(stdlib_new_delete.cpp.obj): definition of _Znam ``` The executable contains a definition from `libc++.a` while at run-time, libfdio.so's `_Znam` reference gets resolved to `libclang_rt.asan.so`. This scenarios is often undesired. In this case, a possible improvement is to switch to an asan-instrumented libc++.a that does not define `_Znam`. I have also seen problems due to mixing multiple definitions from `libgcc.a` (hidden visibility) and `libclang_rt.builtins.a` (default visibility) and relying on archive member extraction rules to work. --- * I wonder whether this option is useful enough to justify a new option. * Using protected visibility can cause similar multiple definition issues. Is it useful to generalize this option?
For a Defined/Common symbol with a false `inDynamicList`, both `versionId==VER_NDX_LOCAL` and `!exportDynamic` indicate that the symbol should not be exported, which means that the two fields have overlapping purposes. We can merge them together by reserving `versionId==-1` to indicate a symbol that is not exported: * -1 (initial): not exported, binding unchanged * 0: VER_NDX_LOCAL, not exported, binding changed to STB_LOCAL * 1: VER_NDX_GLOBAL, exported, binding unchanged * others: verdef index, exported, binding unchanged -1 and 0 are similar, but -1 does not change the binding to STB_LOCAL. The saved bit can be use for another purpose, e.g. whether a symbol has a DSO definition (llvm#70130). --version-script is almost never used for executables. If used, a minor behavior change is that a version pattern that is not `local:` will export the matched symbols.
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for version script patterns and `sym@ver` symbols. `verdefIndex` is the Verdef index of a SharedSymbol. The default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for llvm#70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
5f6dd33
to
90bbca0
Compare
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for version script patterns and `sym@ver` symbols. `verdefIndex` is the Verdef index of a SharedSymbol. The default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for #70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
90bbca0
to
a164f8f
Compare
Rebase after "[ELF] Merge verdefIndex into versionId. NFC (#72208)" (I managed to find a way to use a regular bitfield, not an |
a164f8f
to
11a9a7c
Compare
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for version script patterns and `sym@ver` symbols. `verdefIndex` is the Verdef index of a SharedSymbol. The default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for llvm#70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for `--exclude-id=`, version script patterns, and `sym@ver` symbols. `verdefIndex` is the Verdef index of a Sharedfile (SharedSymbol or a copy-relocated Defined), the default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for #70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for `--exclude-id=`, version script patterns, and `sym@ver` symbols. `verdefIndex` is the Verdef index of a Sharedfile (SharedSymbol or a copy-relocated Defined), the default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for llvm#70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for version script patterns and `sym@ver` symbols. `verdefIndex` is the Verdef index of a SharedSymbol. The default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for llvm#70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
The two fields are similar. `versionId` is the Verdef index in the output file. It is set for `--exclude-id=`, version script patterns, and `sym@ver` symbols. `verdefIndex` is the Verdef index of a Sharedfile (SharedSymbol or a copy-relocated Defined), the default value -1 is also used to indicate that the symbol has not been matched by a version script pattern (https://reviews.llvm.org/D65716). It seems confusing to have two fields. Merge them so that we can allocate one bit for llvm#70130 (suppress --no-allow-shlib-undefined error in the presence of a DSO definition).
Ping. I think we need this for 18.1 release, but probably not need #70163 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to remind myself of where we were up to by re-reading https://discourse.llvm.org/t/relocatable-file-definitions-shared-with-dso/74391/7
I'm guessing the main reason to make the change is that an error message has been tightened and this might surprise some users picking up the next official release of LLD for the first time.
IIUC this restores the previous behaviour, and #70163 builds on top of this (but adds a new option) that we are not sure is worth the additional complexity.
Overall LGTM.
I do have one question about the description:
* There is a DSO undef that can be satisfied by a definition from another DSO.
Should this be
* There is a DSO undef that has been satisfied by a definition from another DSO.
The use of "can be satisfied" implies that we can have:
- DSO undef of sym
foo
. - non-exported Defined
foo
. - Section containing Defined is garbage collected.
In that case there is no definition offoo
available for the DSO undef so we should give an error.
Will be worth updating the discourse thread (https://discourse.llvm.org/t/relocatable-file-definitions-shared-with-dso/74391) when the commit is merged. |
…bol is overridden by a hidden visibility Defined which is later discarded Commit 1981b1b unexpectedly strengthened --no-allow-shlib-undefined to catch a kind of ODR violation. More precisely, when all three conditions are met, the new `--no-allow-shlib-undefined` code reports an error. * There is a DSO undef that has been satisfied by a definition from another DSO. * The `SharedSymbol` is overridden by a non-exported (usually of hidden visibility) definition in a relocatable object file (`Defined`). * The section containing the `Defined` is garbage-collected (it is not part of `.dynsym` and is not marked as live). Technically, the hidden Defined in the executable can be intentional: it can be meant to remain non-exported and not interact with any dynamic symbols of the same name that might exist in other DSOs. To allow for such use cases, allocate a new bit in Symbol and relax the --no-allow-shlib-undefined check to before commit 1981b1b.
11a9a7c
to
4f22a68
Compare
Thanks for the wording suggestion. Updated. Merged in e390bda Made a reply to https://discourse.llvm.org/t/relocatable-file-definitions-shared-with-dso/74391/10 |
Commit 1981b1b unexpectedly strengthened
--no-allow-shlib-undefined to catch a kind of ODR violation.
More precisely, when all three conditions are met, the new
--no-allow-shlib-undefined
code reports an error.SharedSymbol
is overridden by a non-exported (usually of hidden visibility) definition in a relocatable object file (Defined
).Defined
is garbage-collected (it is not part of.dynsym
and is not marked as live).Technically, the hidden Defined in the executable can be intentional: it can be
meant to remain non-exported and not interact with any dynamic symbols of the same
name that might exist in other DSOs. To allow for such use cases, allocate a new bit in
Symbol and relax the --no-allow-shlib-undefined check to before commit 1981b1b.