[ELF] Move TLS mismatch error from Symbol::replace to postParse

* detect `def_tls.o undef_nontls.o` violation
* place error checking code (checking duplicate symbol) together
* allow `--defsym tls1=tls2 def_tls.o`

As a degraded error checking, `--defsym tls1=42` violation will not be detected.
This commit is contained in:
Fangrui Song 2022-02-23 20:34:48 -08:00
parent 338b478e70
commit ba061713d3
3 changed files with 16 additions and 19 deletions

View File

@ -1156,6 +1156,16 @@ template <class ELFT> void ObjFile<ELFT>::postParse() {
for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
const Elf_Sym &eSym = eSyms[i];
const Symbol &sym = *symbols[i];
// st_value of STT_TLS represents the assigned offset, not the actual
// address which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can
// only be referenced by special TLS relocations. It is usually an error if
// a STT_TLS symbol is replaced by a non-STT_TLS symbol, vice versa.
if (LLVM_UNLIKELY(sym.isTls()) && eSym.getType() != STT_TLS &&
eSym.getType() != STT_NOTYPE)
errorOrWarn("TLS attribute mismatch: " + toString(sym) + "\n>>> in " +
toString(sym.file) + "\n>>> in " + toString(this));
// !sym.file allows a symbol assignment redefines a symbol without an error.
if (sym.file == this || !sym.file || !sym.isDefined() ||
eSym.st_shndx == SHN_UNDEF || eSym.st_shndx == SHN_COMMON ||

View File

@ -523,21 +523,6 @@ size_t Symbol::getSymbolSize() const {
// it over to "this". This function is called as a result of name
// resolution, e.g. to replace an undefind symbol with a defined symbol.
void Symbol::replace(const Symbol &other) {
using llvm::ELF::STT_TLS;
// st_value of STT_TLS represents the assigned offset, not the actual address
// which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can only be
// referenced by special TLS relocations. It is usually an error if a STT_TLS
// symbol is replaced by a non-STT_TLS symbol, vice versa. There are two
// exceptions: (a) a STT_NOTYPE lazy/undefined symbol can be replaced by a
// STT_TLS symbol, (b) a STT_TLS undefined symbol can be replaced by a
// STT_NOTYPE lazy symbol.
if (symbolKind != PlaceholderKind && !other.isLazy() &&
(type == STT_TLS) != (other.type == STT_TLS) &&
type != llvm::ELF::STT_NOTYPE)
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
toString(other.file) + "\n>>> defined in " + toString(file));
Symbol old = *this;
memcpy(this, &other, other.getSymbolSize());

View File

@ -9,8 +9,7 @@
## The TLS definition mismatches a non-TLS reference.
# RUN: echo '.type tls1,@object; movq tls1,%rax' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
# RUN: not ld.lld %t2.o %t.o -o /dev/null 2>&1 | FileCheck %s
## We fail to flag the swapped case.
# RUN: ld.lld %t.o %t2.o -o /dev/null
# RUN: not ld.lld %t.o %t2.o -o /dev/null 2>&1 | FileCheck %s
## We fail to flag the STT_NOTYPE reference. This usually happens with hand-written
## assembly because compiler-generated code properly sets symbol types.
@ -18,10 +17,11 @@
# RUN: ld.lld %t3.o %t.o -o /dev/null
## Overriding a TLS definition with a non-TLS definition does not make sense.
# RUN: not ld.lld --defsym tls1=42 %t.o -o /dev/null 2>&1 | FileCheck %s
## We fail to flag this case.
# RUN: ld.lld --defsym tls1=42 %t.o -o /dev/null 2>&1 | count 0
## Part of PR36049: This should probably be allowed.
# RUN: not ld.lld --defsym tls1=tls2 %t.o -o /dev/null 2>&1 | FileCheck %s
# RUN: ld.lld --defsym tls1=tls2 %t.o -o /dev/null 2>&1 | count 0
## An undefined symbol in module-level inline assembly of a bitcode file
## is considered STT_NOTYPE. We should not error.
@ -32,6 +32,8 @@
# RUN: ld.lld %t.bc %t.o -o /dev/null
# CHECK: error: TLS attribute mismatch: tls1
# CHECK-NEXT: >>> in {{.*}}.tmp.o
# CHECK-NEXT: >>> in {{.*}}
.globl _start
_start: