Skip to content

Commit 655a146

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 f0815ea commit 655a146

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

lld/COFF/Writer.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ class Writer {
234234
void addSyntheticIdata();
235235
void fixPartialSectionChars(StringRef name, uint32_t chars);
236236
bool fixGnuImportChunks();
237+
void fixTlsAlignment();
237238
PartialSection *createPartialSection(StringRef name, uint32_t outChars);
238239
PartialSection *findPartialSection(StringRef name, uint32_t outChars);
239240

@@ -260,6 +261,7 @@ class Writer {
260261
DelayLoadContents delayIdata;
261262
EdataContents edata;
262263
bool setNoSEHCharacteristic = false;
264+
uint32_t tlsAlignment = 0;
263265

264266
DebugDirectoryChunk *debugDirectory = nullptr;
265267
std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
@@ -628,6 +630,11 @@ void Writer::run() {
628630
writeSections();
629631
sortExceptionTable();
630632

633+
// Fix up the alignment in the TLS Directory's characteristic field,
634+
// if a specific alignment value is needed
635+
if (tlsAlignment)
636+
fixTlsAlignment();
637+
631638
t1.stop();
632639

633640
if (!config->pdbPath.empty() && config->debug) {
@@ -848,6 +855,10 @@ void Writer::createSections() {
848855
StringRef name = c->getSectionName();
849856
if (shouldStripSectionSuffix(sc, name))
850857
name = name.split('$').first;
858+
859+
if (name.startswith(".tls"))
860+
tlsAlignment = std::max(tlsAlignment, c->getAlignment());
861+
851862
PartialSection *pSec = createPartialSection(name,
852863
c->getOutputCharacteristics());
853864
pSec->chunks.push_back(c);
@@ -1993,3 +2004,33 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
19932004
return it->second;
19942005
return nullptr;
19952006
}
2007+
2008+
void Writer::fixTlsAlignment() {
2009+
Defined *tlsSym =
2010+
dyn_cast_or_null<Defined>(symtab->findUnderscore("_tls_used"));
2011+
if (!tlsSym)
2012+
return;
2013+
2014+
OutputSection *sec = tlsSym->getChunk()->getOutputSection();
2015+
assert(sec && tlsSym->getRVA() >= sec->getRVA() &&
2016+
"no output section for _tls_used");
2017+
2018+
uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
2019+
uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();
2020+
uint64_t directorySize = config->is64()
2021+
? sizeof(object::coff_tls_directory64)
2022+
: sizeof(object::coff_tls_directory32);
2023+
2024+
if (tlsOffset + directorySize > sec->getRawSize())
2025+
fatal("_tls_used sym is malformed");
2026+
2027+
if (config->is64()) {
2028+
object::coff_tls_directory64 *tlsDir =
2029+
reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
2030+
tlsDir->setAlignment(tlsAlignment);
2031+
} else {
2032+
object::coff_tls_directory32 *tlsDir =
2033+
reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]);
2034+
tlsDir->setAlignment(tlsAlignment);
2035+
}
2036+
}

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

+2-1
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

+2-1
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

+12-1
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)