Skip to content

Commit 6a73d65

Browse files
committed
[LLD] Set alignment as part of Characteristics in TLS table.
Fixes https://bugs.llvm.org/show_bug.cgi?id=46473 LLD wasn't previously specifying any specific alignment in the TLS table's Characteristics field so the loader would just assume the default value (16 bytes). This works most of the time except if you have thread locals that want specific higher alignments (e.g. 32 as in the bug) *even* if they specify an alignment on the thread local. This change updates LLD to take the max alignment from tls section. Reviewed By: rnk Differential Revision: https://reviews.llvm.org/D88637
1 parent f87c98d commit 6a73d65

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

lld/COFF/Writer.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ class Writer {
241241
void addSyntheticIdata();
242242
void fixPartialSectionChars(StringRef name, uint32_t chars);
243243
bool fixGnuImportChunks();
244+
void fixTlsAlignment();
244245
PartialSection *createPartialSection(StringRef name, uint32_t outChars);
245246
PartialSection *findPartialSection(StringRef name, uint32_t outChars);
246247

@@ -267,6 +268,7 @@ class Writer {
267268
DelayLoadContents delayIdata;
268269
EdataContents edata;
269270
bool setNoSEHCharacteristic = false;
271+
uint32_t tlsAlignment = 0;
270272

271273
DebugDirectoryChunk *debugDirectory = nullptr;
272274
std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
@@ -633,6 +635,11 @@ void Writer::run() {
633635
writeSections();
634636
sortExceptionTable();
635637

638+
// Fix up the alignment in the TLS Directory's characteristic field,
639+
// if a specific alignment value is needed
640+
if (tlsAlignment)
641+
fixTlsAlignment();
642+
636643
t1.stop();
637644

638645
if (!config->pdbPath.empty() && config->debug) {
@@ -866,6 +873,10 @@ void Writer::createSections() {
866873
StringRef name = c->getSectionName();
867874
if (shouldStripSectionSuffix(sc, name))
868875
name = name.split('$').first;
876+
877+
if (name.startswith(".tls"))
878+
tlsAlignment = std::max(tlsAlignment, c->getAlignment());
879+
869880
PartialSection *pSec = createPartialSection(name,
870881
c->getOutputCharacteristics());
871882
pSec->chunks.push_back(c);
@@ -2038,3 +2049,33 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
20382049
return it->second;
20392050
return nullptr;
20402051
}
2052+
2053+
void Writer::fixTlsAlignment() {
2054+
Defined *tlsSym =
2055+
dyn_cast_or_null<Defined>(symtab->findUnderscore("_tls_used"));
2056+
if (!tlsSym)
2057+
return;
2058+
2059+
OutputSection *sec = tlsSym->getChunk()->getOutputSection();
2060+
assert(sec && tlsSym->getRVA() >= sec->getRVA() &&
2061+
"no output section for _tls_used");
2062+
2063+
uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
2064+
uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();
2065+
uint64_t directorySize = config->is64()
2066+
? sizeof(object::coff_tls_directory64)
2067+
: sizeof(object::coff_tls_directory32);
2068+
2069+
if (tlsOffset + directorySize > sec->getRawSize())
2070+
fatal("_tls_used sym is malformed");
2071+
2072+
if (config->is64()) {
2073+
object::coff_tls_directory64 *tlsDir =
2074+
reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
2075+
tlsDir->setAlignment(tlsAlignment);
2076+
} else {
2077+
object::coff_tls_directory32 *tlsDir =
2078+
reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]);
2079+
tlsDir->setAlignment(tlsAlignment);
2080+
}
2081+
}

lld/test/COFF/tls-alignment-32.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
; RUN: llvm-readobj --coff-tls-directory %t.exe | FileCheck %s
1414

1515
; CHECK: TLSDirectory {
16-
; CHECK: Characteristics [ (0x0)
16+
; CHECK: Characteristics [ (0x600000)
17+
; CHECK-NEXT: IMAGE_SCN_ALIGN_32BYTES (0x600000)
1718

1819
target triple = "i686-pc-windows-msvc"
1920

lld/test/COFF/tls-alignment-64.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
; RUN: llvm-readobj --coff-tls-directory %t.exe | FileCheck %s
1414

1515
; CHECK: TLSDirectory {
16-
; CHECK: Characteristics [ (0x0)
16+
; CHECK: Characteristics [ (0x700000)
17+
; CHECK-NEXT: IMAGE_SCN_ALIGN_64BYTES (0x700000)
1718

1819
target triple = "x86_64-pc-windows-msvc"
1920

llvm/include/llvm/Object/COFF.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,11 +576,22 @@ struct coff_tls_directory {
576576

577577
uint32_t getAlignment() const {
578578
// Bit [20:24] contains section alignment.
579-
uint32_t Shift = (Characteristics & 0x00F00000) >> 20;
579+
uint32_t Shift = (Characteristics & COFF::IMAGE_SCN_ALIGN_MASK) >> 20;
580580
if (Shift > 0)
581581
return 1U << (Shift - 1);
582582
return 0;
583583
}
584+
585+
void setAlignment(uint32_t Align) {
586+
uint32_t AlignBits = 0;
587+
if (Align) {
588+
assert(llvm::isPowerOf2_32(Align) && "alignment is not a power of 2");
589+
assert(llvm::Log2_32(Align) <= 13 && "alignment requested is too large");
590+
AlignBits = (llvm::Log2_32(Align) + 1) << 20;
591+
}
592+
Characteristics =
593+
(Characteristics & ~COFF::IMAGE_SCN_ALIGN_MASK) | AlignBits;
594+
}
584595
};
585596

586597
using coff_tls_directory32 = coff_tls_directory<support::little32_t>;

0 commit comments

Comments
 (0)