[llvm-branch-commits] [lld] 4783a6c - [ELF] Combine foo at v1 and foo with the same versionId if both are defined
Fangrui Song via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 4 09:19:09 PDT 2021
Author: Fangrui Song
Date: 2021-08-04T09:18:10-07:00
New Revision: 4783a6cdf0a9087212f517ce20e0e3a9cf39d6a4
URL: https://github.com/llvm/llvm-project/commit/4783a6cdf0a9087212f517ce20e0e3a9cf39d6a4
DIFF: https://github.com/llvm/llvm-project/commit/4783a6cdf0a9087212f517ce20e0e3a9cf39d6a4.diff
LOG: [ELF] Combine foo at v1 and foo with the same versionId if both are defined
Due to an assembler design flaw (IMO), `.symver foo,foo at v1` produces two symbols `foo` and `foo at v1` if `foo` is defined.
* `v1 {};` produces both `foo` and `foo at v1`, but GNU ld only produces `foo at v1`
* `v1 { foo; };` produces both `foo@@v1` and `foo at v1`, but GNU ld only produces `foo at v1`
* `v2 { foo; };` produces both `foo@@v2` and `foo at v1`, matching GNU ld. (Tested by symver.s)
This patch implements the GNU ld behavior by reusing the symbol redirection mechanism
in D92259. The new test symver-non-default.s checks the first two cases.
Without the patch, the second case will produce `foo at v1` and `foo@@v1` which
looks weird and makes foo unnecessarily default versioned.
Note: `.symver foo,foo at v1,remove` exists but the unfortunate `foo` will not go
away anytime soon.
Reviewed By: peter.smith
Differential Revision: https://reviews.llvm.org/D107235
Added:
lld/test/ELF/symver-non-default.s
Modified:
lld/ELF/Driver.cpp
lld/test/ELF/version-script-symver.s
lld/test/ELF/version-symbol-undef.s
Removed:
################################################################################
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 4197c6b115bb..594c20016827 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2070,23 +2070,37 @@ static void redirectSymbols(ArrayRef<WrappedSymbol> wrapped) {
if (suffix1[0] != '@' || suffix1[1] == '@')
continue;
- // Check whether the default version foo@@v1 exists. If it exists, the
- // symbol can be found by the name "foo" in the symbol table.
- Symbol *maybeDefault = symtab->find(name);
- if (!maybeDefault)
- continue;
- const char *suffix2 = maybeDefault->getVersionSuffix();
- if (suffix2[0] != '@' || suffix2[1] != '@' ||
- strcmp(suffix1 + 1, suffix2 + 2) != 0)
+ // Check the existing symbol foo. We have two special cases to handle:
+ //
+ // * There is a definition of foo at v1 and foo@@v1.
+ // * There is a definition of foo at v1 and foo.
+ Defined *sym2 = dyn_cast_or_null<Defined>(symtab->find(name));
+ if (!sym2)
continue;
-
- // foo at v1 and foo@@v1 should be merged, so redirect foo at v1 to foo@@v1.
- map.try_emplace(sym, maybeDefault);
- // If both foo at v1 and foo@@v1 are defined and non-weak, report a duplicate
- // definition error.
- maybeDefault->resolve(*sym);
- // Eliminate foo at v1 from the symbol table.
- sym->symbolKind = Symbol::PlaceholderKind;
+ const char *suffix2 = sym2->getVersionSuffix();
+ if (suffix2[0] == '@' && suffix2[1] == '@' &&
+ strcmp(suffix1 + 1, suffix2 + 2) == 0) {
+ // foo at v1 and foo@@v1 should be merged, so redirect foo at v1 to foo@@v1.
+ map.try_emplace(sym, sym2);
+ // If both foo at v1 and foo@@v1 are defined and non-weak, report a duplicate
+ // definition error.
+ sym2->resolve(*sym);
+ // Eliminate foo at v1 from the symbol table.
+ sym->symbolKind = Symbol::PlaceholderKind;
+ } else if (auto *sym1 = dyn_cast<Defined>(sym)) {
+ if (sym2->versionId > VER_NDX_GLOBAL
+ ? config->versionDefinitions[sym2->versionId].name == suffix1 + 1
+ : sym1->section == sym2->section && sym1->value == sym2->value) {
+ // Due to an assembler design flaw, if foo is defined, .symver foo,
+ // foo at v1 defines both foo and foo at v1. Unless foo is bound to a
+ //
diff erent version, GNU ld makes foo at v1 canonical and elimiates foo.
+ // Emulate its behavior, otherwise we would have foo or foo@@v1 beside
+ // foo at v1. foo at v1 and foo combining does not apply if they are not
+ // defined in the same place.
+ map.try_emplace(sym2, sym);
+ sym2->symbolKind = Symbol::PlaceholderKind;
+ }
+ }
}
if (map.empty())
diff --git a/lld/test/ELF/symver-non-default.s b/lld/test/ELF/symver-non-default.s
new file mode 100644
index 000000000000..4923a5108018
--- /dev/null
+++ b/lld/test/ELF/symver-non-default.s
@@ -0,0 +1,69 @@
+# REQUIRES: x86
+## Test symbol resolution related to .symver produced non-default version symbols.
+
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/ref.s -o %t/ref.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/def1.s -o %t/def1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/def2.s -o %t/def2.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/def3.s -o %t/def3.o
+
+## foo at v1 & foo defined at the same location are combined.
+# RUN: ld.lld -shared --version-script=%t/ver1 %t/def1.o %t/ref.o -o %t1
+# RUN: llvm-readelf --dyn-syms %t1 | FileCheck %s --check-prefix=CHECK1
+# RUN: ld.lld -shared --version-script=%t/ver2 %t/def1.o %t/ref.o -o %t1
+# RUN: llvm-readelf --dyn-syms %t1 | FileCheck %s --check-prefix=CHECK1
+
+# CHECK1: 1: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo at v1
+# CHECK1-EMPTY:
+
+## def2.o doesn't define foo. foo at v1 & undefined foo are unrelated.
+# RUN: ld.lld -shared --version-script=%t/ver1 %t/def2.o %t/ref.o -o %t2
+# RUN: llvm-readelf -r --dyn-syms %t2 | FileCheck %s --check-prefix=CHECK2
+
+# CHECK2: R_X86_64_JUMP_SLOT {{.*}} foo + 0
+# CHECK2: 1: {{.*}} NOTYPE GLOBAL DEFAULT UND foo{{$}}
+# CHECK2-NEXT: 2: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo at v1
+# CHECK2-EMPTY:
+
+## def2.o doesn't define foo. foo at v1 & defined foo are unrelated.
+# RUN: ld.lld -shared --version-script=%t/ver1 %t/def2.o %t/def3.o %t/ref.o -o %t3
+# RUN: llvm-readelf -r --dyn-syms %t3 | FileCheck %s --check-prefix=CHECK3
+
+# CHECK3: R_X86_64_JUMP_SLOT {{.*}} foo + 0
+# CHECK3: 1: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo at v1
+# CHECK3-NEXT: 2: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo{{$}}
+# CHECK3-EMPTY:
+
+## foo at v1 overrides the defined foo which is affected by a version script.
+# RUN: ld.lld -shared --version-script=%t/ver2 %t/def2.o %t/def3.o %t/ref.o -o %t4
+# RUN: llvm-readelf -r --dyn-syms %t4 | FileCheck %s --check-prefix=CHECK4
+
+# CHECK4: R_X86_64_JUMP_SLOT {{.*}} foo at v1 + 0
+# CHECK4: 1: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo at v1
+# CHECK4-EMPTY:
+
+#--- ver1
+v1 {};
+
+#--- ver2
+v1 { foo; };
+
+#--- ref.s
+call foo
+
+#--- def1.s
+.globl foo
+.symver foo, foo at v1
+foo:
+ ret
+
+#--- def2.s
+.globl foo_v1
+.symver foo_v1, foo at v1, remove
+foo_v1:
+ ret
+
+#--- def3.s
+.globl foo
+foo:
+ ret
diff --git a/lld/test/ELF/version-script-symver.s b/lld/test/ELF/version-script-symver.s
index b26c304c358a..a3a820f9edea 100644
--- a/lld/test/ELF/version-script-symver.s
+++ b/lld/test/ELF/version-script-symver.s
@@ -1,12 +1,13 @@
# REQUIRES: x86
## Test how .symver interacts with --version-script.
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: echo 'call foo3; call foo4' > %tref.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %tref.s -o %tref.o
# RUN: echo 'v1 { local: foo1; }; v2 { local: foo2; };' > %t1.script
# RUN: ld.lld --version-script %t1.script -shared %t.o -o %t1.so
# RUN: llvm-readelf --dyn-syms %t1.so | FileCheck --check-prefix=EXACT %s
# EXACT: UND
-# EXACT-NEXT: [[#]] foo3{{$}}
# EXACT-NEXT: [[#]] foo4@@v2
# EXACT-NEXT: [[#]] _start{{$}}
# EXACT-NEXT: [[#]] foo3 at v1
@@ -34,11 +35,16 @@
# MIX2: UND
# MIX2-NEXT: [[#]] foo1@@v1
# MIX2-NEXT: [[#]] foo2@@v1
-# MIX2-NEXT: [[#]] foo3@@v1
# MIX2-NEXT: [[#]] foo4@@v2
# MIX2-NEXT: [[#]] foo3 at v1
# MIX2-NOT: {{.}}
+# RUN: ld.lld --version-script %t4.script -shared %t.o %tref.o -o %t5.so
+# RUN: llvm-readelf -r %t5.so | FileCheck --check-prefix=RELOC %s
+
+# RELOC: R_X86_64_JUMP_SLOT {{.*}} foo3 at v1 + 0
+# RELOC: R_X86_64_JUMP_SLOT {{.*}} foo4@@v2 + 0
+
.globl foo1; foo1: ret
.globl foo2; foo2: ret
.globl foo3; .symver foo3,foo3 at v1; foo3: ret
diff --git a/lld/test/ELF/version-symbol-undef.s b/lld/test/ELF/version-symbol-undef.s
index 22b943a5034e..ff1e7a51ef2e 100644
--- a/lld/test/ELF/version-symbol-undef.s
+++ b/lld/test/ELF/version-symbol-undef.s
@@ -5,7 +5,7 @@
// RUN: .quad \"basename at FBSD_1.1\" " > %t.s
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t.s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
-// RUN: echo "FBSD_1.0 { global: basename; local: *; }; FBSD_1.1 { };" > %t2.ver
+// RUN: echo "FBSD_1.0 { global: basename; local: *; }; FBSD_1.1 { basename; };" > %t2.ver
// RUN: ld.lld --shared --version-script %t2.ver %t2.o -o %t2.so
// RUN: echo "FBSD_1.0 { }; FBSD_1.1 { }; LIBPKG_1.3 { };" > %t.ver
// RUN: ld.lld --shared %t.o --version-script %t.ver %t2.so -o %t.so
More information about the llvm-branch-commits
mailing list