diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -484,7 +484,8 @@ SmallSet ret; for (const Elf_Sym &s : file.template getGlobalELFSyms()) { if (s.st_shndx == SHN_UNDEF || s.st_shndx == SHN_ABS || - s.getType() == STT_TLS || s.st_value != ss.value) + (s.getType() == STT_TLS) != ss.isTls() || + s.st_value != ss.value) continue; StringRef name = check(s.getName(file.getStringTable())); Symbol *sym = symtab->find(name); @@ -563,11 +564,15 @@ fatal("cannot create a copy relocation for symbol " + toString(ss)); // See if this symbol is in a read-only segment. If so, preserve the symbol's - // memory protection by reserving space in the .bss.rel.ro section. + // memory protection by reserving space in the .bss.rel.ro section. For TLS + // symbols, .tbss can always be relro. + bool isTbss = ss.isTls(); bool isRO = isReadOnly(ss); BssSection *sec = - make(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment); - OutputSection *osec = (isRO ? in.bssRelRo : in.bss)->getParent(); + make(isTbss ? ".tbss" : isRO ? ".bss.rel.ro" : ".bss", + symSize, ss.alignment, isTbss); + OutputSection *osec = + (isTbss ? in.tbss : isRO ? in.bssRelRo : in.bss)->getParent(); // At this point, sectionBases has been migrated to sections. Append sec to // sections. @@ -1135,7 +1140,7 @@ return; } - if (sym.isObject()) { + if (sym.isObject() || sym.isTls()) { // Produce a copy relocation. if (auto *ss = dyn_cast(&sym)) { if (!config->zCopyreloc) diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -177,10 +177,11 @@ // BssSection is used to reserve space for copy relocations and common symbols. // We create three instances of this class for .bss, .bss.rel.ro and "COMMON", // that are used for writable symbols, read-only symbols and common symbols, -// respectively. +// respectively, and one instance for .tbss used for thread-local symbols. class BssSection final : public SyntheticSection { public: - BssSection(StringRef name, uint64_t size, uint32_t alignment); + BssSection(StringRef name, uint64_t size, uint32_t alignment, + bool isTbss = false); void writeTo(uint8_t *) override { llvm_unreachable("unexpected writeTo() call for SHT_NOBITS section"); } @@ -1151,6 +1152,7 @@ InputSection *armAttributes; BssSection *bss; BssSection *bssRelRo; + BssSection *tbss; GotSection *got; GotPltSection *gotPlt; IgotPltSection *igotPlt; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -336,8 +336,11 @@ memcpy(hashBuf, buf.data(), hashSize); } -BssSection::BssSection(StringRef name, uint64_t size, uint32_t alignment) - : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, alignment, name) { +BssSection::BssSection(StringRef name, uint64_t size, uint32_t alignment, + bool isTbss) + : SyntheticSection(isTbss ? SHF_ALLOC | SHF_WRITE | SHF_TLS + : SHF_ALLOC | SHF_WRITE, + SHT_NOBITS, alignment, name) { this->bss = true; this->size = size; } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -349,6 +349,9 @@ make(hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1); add(in.bssRelRo); + in.tbss = make(".tbss", 0, 1, /*isTbss=*/true); + add(in.tbss); + // Add MIPS-specific sections. if (config->emachine == EM_MIPS) { if (!config->shared && config->hasDynSymTab) { @@ -1874,6 +1877,7 @@ finalizeSynthetic(in.bss); finalizeSynthetic(in.bssRelRo); + finalizeSynthetic(in.tbss); finalizeSynthetic(in.symTabShndx); finalizeSynthetic(in.shStrTab); finalizeSynthetic(in.strTab); diff --git a/lld/test/ELF/linkerscript/orphan-report.s b/lld/test/ELF/linkerscript/orphan-report.s --- a/lld/test/ELF/linkerscript/orphan-report.s +++ b/lld/test/ELF/linkerscript/orphan-report.s @@ -19,6 +19,7 @@ # REPORT-NEXT: :(.comment) is being placed in '.comment' # REPORT-NEXT: :(.bss) is being placed in '.bss' # REPORT-NEXT: :(.bss.rel.ro) is being placed in '.bss.rel.ro' +# REPORT-NEXT: :(.tbss) is being placed in '.tbss' # REPORT-NEXT: :(.dynsym) is being placed in '.dynsym' # REPORT-NEXT: :(.gnu.version) is being placed in '.gnu.version' # REPORT-NEXT: :(.gnu.version_r) is being placed in '.gnu.version_r' diff --git a/lld/test/ELF/riscv-tls-le-copy.s b/lld/test/ELF/riscv-tls-le-copy.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-tls-le-copy.s @@ -0,0 +1,51 @@ +# REQUIRES: riscv +# RUN: echo '.tbss; .globl a, b; a: .zero 4; .size a, 4; b: .zero 4; .size b, 4' > %t.s + +## Check that we correctly emit a copy relocation if using LE to access a +## symbol not in the executable. + +# RUN: llvm-mc -filetype=obj -triple=riscv32 %s -o %t.32.o +# RUN: llvm-mc -filetype=obj -triple=riscv32 %t.s -o %t1.32.o +# RUN: ld.lld -shared -soname=t1.so %t1.32.o -o %t1.32.so +# RUN: ld.lld %t.32.o %t1.32.so -o %t.32 +# RUN: llvm-readobj -S -r %t.32 | FileCheck --check-prefix=LE-SECREL %s +# RUN: llvm-objdump -d %t.32 | FileCheck --check-prefix=LE-DUMP %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.64.o +# RUN: llvm-mc -filetype=obj -triple=riscv64 %t.s -o %t1.64.o +# RUN: ld.lld -shared -soname=t1.so %t1.64.o -o %t1.64.so +# RUN: ld.lld %t.64.o %t1.64.so -o %t.64 +# RUN: llvm-readobj -S -r %t.64 | FileCheck --check-prefix=LE-SECREL %s +# RUN: llvm-objdump -d %t.64 | FileCheck --check-prefix=LE-DUMP %s + +# LE-SECREL: Name: .tbss +# LE-SECREL-NEXT: Type: SHT_NOBITS +# LE-SECREL-NEXT: Flags [ (0x403) +# LE-SECREL-NEXT: SHF_ALLOC (0x2) +# LE-SECREL-NEXT: SHF_TLS (0x400) +# LE-SECREL-NEXT: SHF_WRITE (0x1) +# LE-SECREL-NEXT: ] +# LE-SECREL-NEXT: Address: 0x[[TBSS:.*]] +# LE-SECREL-NEXT: Offset: +# LE-SECREL-NEXT: Size: 8 + +# LE-SECREL: .rela.dyn { +# LE-SECREL-NEXT: 0x[[TBSS]] R_RISCV_COPY a 0x0 +# LE-SECREL-NEXT: R_RISCV_COPY b 0x0 +# LE-SECREL-NEXT: } + +# LE-DUMP: lui a5, 0 +# LE-DUMP-NEXT: add a5, a5, tp +# LE-DUMP-NEXT: sw a0, 0(a5) + +# LE-DUMP: lui a5, 0 +# LE-DUMP-NEXT: add a5, a5, tp +# LE-DUMP-NEXT: sw a0, 4(a5) + +lui a5, %tprel_hi(a) +add a5, a5, tp, %tprel_add(a) +sw a0, %tprel_lo(a)(a5) + +lui a5, %tprel_hi(b) +add a5, a5, tp, %tprel_add(b) +sw a0, %tprel_lo(b)(a5) diff --git a/lld/test/ELF/shared.s b/lld/test/ELF/shared.s --- a/lld/test/ELF/shared.s +++ b/lld/test/ELF/shared.s @@ -46,7 +46,7 @@ // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: ] // CHECK-NEXT: Address: [[DYNSYMADDR:.*]] -// CHECK-NEXT: Offset: 0x170 +// CHECK-NEXT: Offset: // CHECK-NEXT: Size: // CHECK-NEXT: Link: [[DYNSTR:.*]] // CHECK-NEXT: Info: 1 @@ -141,7 +141,7 @@ // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: _DYNAMIC -// CHECK-NEXT: Value: 0x402210 +// CHECK-NEXT: Value: // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None @@ -152,7 +152,7 @@ // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: _start -// CHECK-NEXT: Value: 0x401208 +// CHECK-NEXT: Value: // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: None @@ -191,7 +191,7 @@ // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: _start -// CHECK-NEXT: Value: 0x401208 +// CHECK-NEXT: Value: // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: Non