Skip to content

Commit 90b2a15

Browse files
luqmananikic
authored andcommitted
[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 76e16be commit 90b2a15

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
@@ -236,6 +236,7 @@ class Writer {
236236
void addSyntheticIdata();
237237
void fixPartialSectionChars(StringRef name, uint32_t chars);
238238
bool fixGnuImportChunks();
239+
void fixTlsAlignment();
239240
PartialSection *createPartialSection(StringRef name, uint32_t outChars);
240241
PartialSection *findPartialSection(StringRef name, uint32_t outChars);
241242

@@ -262,6 +263,7 @@ class Writer {
262263
DelayLoadContents delayIdata;
263264
EdataContents edata;
264265
bool setNoSEHCharacteristic = false;
266+
uint32_t tlsAlignment = 0;
265267

266268
DebugDirectoryChunk *debugDirectory = nullptr;
267269
std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
@@ -630,6 +632,11 @@ void Writer::run() {
630632
writeSections();
631633
sortExceptionTable();
632634

635+
// Fix up the alignment in the TLS Directory's characteristic field,
636+
// if a specific alignment value is needed
637+
if (tlsAlignment)
638+
fixTlsAlignment();
639+
633640
t1.stop();
634641

635642
if (!config->pdbPath.empty() && config->debug) {
@@ -863,6 +870,10 @@ void Writer::createSections() {
863870
StringRef name = c->getSectionName();
864871
if (shouldStripSectionSuffix(sc, name))
865872
name = name.split('$').first;
873+
874+
if (name.startswith(".tls"))
875+
tlsAlignment = std::max(tlsAlignment, c->getAlignment());
876+
866877
PartialSection *pSec = createPartialSection(name,
867878
c->getOutputCharacteristics());
868879
pSec->chunks.push_back(c);
@@ -2005,3 +2016,33 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
20052016
return it->second;
20062017
return nullptr;
20072018
}
2019+
2020+
void Writer::fixTlsAlignment() {
2021+
Defined *tlsSym =
2022+
dyn_cast_or_null<Defined>(symtab->findUnderscore("_tls_used"));
2023+
if (!tlsSym)
2024+
return;
2025+
2026+
OutputSection *sec = tlsSym->getChunk()->getOutputSection();
2027+
assert(sec && tlsSym->getRVA() >= sec->getRVA() &&
2028+
"no output section for _tls_used");
2029+
2030+
uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
2031+
uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();
2032+
uint64_t directorySize = config->is64()
2033+
? sizeof(object::coff_tls_directory64)
2034+
: sizeof(object::coff_tls_directory32);
2035+
2036+
if (tlsOffset + directorySize > sec->getRawSize())
2037+
fatal("_tls_used sym is malformed");
2038+
2039+
if (config->is64()) {
2040+
object::coff_tls_directory64 *tlsDir =
2041+
reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
2042+
tlsDir->setAlignment(tlsAlignment);
2043+
} else {
2044+
object::coff_tls_directory32 *tlsDir =
2045+
reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]);
2046+
tlsDir->setAlignment(tlsAlignment);
2047+
}
2048+
}

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

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

1313
; CHECK: TLSDirectory {
14-
; CHECK: Characteristics [ (0x0)
14+
; CHECK: Characteristics [ (0x600000)
15+
; CHECK-NEXT: IMAGE_SCN_ALIGN_32BYTES (0x600000)
1516

1617
target triple = "i686-pc-windows-msvc"
1718

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

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

1313
; CHECK: TLSDirectory {
14-
; CHECK: Characteristics [ (0x0)
14+
; CHECK: Characteristics [ (0x700000)
15+
; CHECK-NEXT: IMAGE_SCN_ALIGN_64BYTES (0x700000)
1516

1617
target triple = "x86_64-pc-windows-msvc"
1718

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)