[ELF] Support copy relocation on non-default version symbols

Copy relocation on a non-default version symbol is unsupported and can crash at
runtime. Fortunately there is a one-line fix which works for most cases:
ensure `getSymbolsAt` unconditionally returns `ss`.

If two non-default version symbols are defined at the same place and both
are copy relocated, our implementation will copy relocated them into different
addresses. The pointer inequality is very unlikely an issue. In GNU ld, copy
relocating version aliases seems to create more pointer inequality problems than
us.

(
In glibc, sys_errlist@GLIBC_2.2.5 sys_errlist@GLIBC_2.3 sys_errlist@GLIBC_2.4
are defined at the same place, but it is unlikely they are all copy relocated in
one executable. Even if so, the variables are read-only and pointer inequality
should not be a problem.
)

Reviewed By: peter.smith

Differential Revision: https://reviews.llvm.org/D107535
This commit is contained in:
Fangrui Song 2021-08-05 10:32:14 -07:00
parent a46bcc60e5
commit 72d070b4db
3 changed files with 49 additions and 19 deletions

View File

@ -527,6 +527,13 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &ss) {
if (auto *alias = dyn_cast_or_null<SharedSymbol>(sym))
ret.insert(alias);
}
// The loop does not check SHT_GNU_verneed, so ret does not contain
// non-default version symbols. If ss has a non-default version, ret won't
// contain ss. Just add ss unconditionally. If a non-default version alias is
// separately copy relocated, it and ss will have different addresses.
// Fortunately this case is impractical and fails with GNU ld as well.
ret.insert(&ss);
return ret;
}

View File

@ -1,11 +1,22 @@
.data
.global foo@v1
.type foo@v1, @object
.size foo@v1, 4
.global foo@@v2
.type foo@@v2, @object
.size foo@@v2, 8
foo@v1:
foo@@v2:
.global foo_v1
.symver foo_v1, foo@v1, remove
.type foo_v1, @object
.size foo_v1, 4
.global foo_v2
.symver foo_v2, foo@v2, remove
.type foo_v2, @object
.size foo_v2, 8
.global foo
.symver foo, foo@@@v3
.type foo, @object
.size foo, 12
foo_v1:
foo_v2:
foo:
.int 0
.int 0
.int 0

View File

@ -1,15 +1,27 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/copy-rel-version.s -o %t1.o
// RUN: echo "v1 {}; v2 {};" > %t.ver
// RUN: ld.lld %t1.o -shared -soname t1.so --version-script=%t.ver -o %t1.so
// RUN: ld.lld %t.o %t1.so -o %t
// RUN: llvm-readobj --symbols %t | FileCheck %s
# REQUIRES: x86
## Copy relocate a versioned symbol which has a versioned alias.
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/copy-rel-version.s -o %t.o
# RUN: echo 'v1 {}; v2 {}; v3 {};' > %t.ver
# RUN: ld.lld %t.o -shared -soname t.so --version-script=%t.ver -o %t.so
## Copy relocate the default version symbol.
# RUN: ld.lld %t1.o %t.so -o %t1
# RUN: llvm-readelf --dyn-syms %t1 | FileCheck %s --check-prefix=CHECK1
# CHECK1: 1: {{.+}} 12 OBJECT GLOBAL DEFAULT [[#]] foo@v3
# CHECK1-EMPTY:
## Copy relocate the non-default version symbol.
# RUN: llvm-objcopy --redefine-sym foo=foo@v1 %t1.o %t2.o
# RUN: ld.lld %t2.o %t.so -o %t2
# RUN: llvm-readelf --dyn-syms %t2 | FileCheck %s --check-prefix=CHECK2
# CHECK2: 1: [[ADDR:[0-9a-f]+]] 4 OBJECT GLOBAL DEFAULT [[#]] foo@v1
# CHECK2-NEXT: 2: [[ADDR]] 12 OBJECT GLOBAL DEFAULT [[#]] foo@v3
# CHECK2-EMPTY:
.global _start
_start:
leaq foo, %rax
// CHECK: Name: foo (
// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 8