diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -238,6 +238,7 @@ void addSyntheticIdata(); void fixPartialSectionChars(StringRef name, uint32_t chars); bool fixGnuImportChunks(); + void fixTlsAlignment(); PartialSection *createPartialSection(StringRef name, uint32_t outChars); PartialSection *findPartialSection(StringRef name, uint32_t outChars); @@ -264,6 +265,7 @@ DelayLoadContents delayIdata; EdataContents edata; bool setNoSEHCharacteristic = false; + uint32_t tlsAlignment = 0; DebugDirectoryChunk *debugDirectory = nullptr; std::vector> debugRecords; @@ -629,6 +631,11 @@ writeSections(); sortExceptionTable(); + // Fix up the alignment in the TLS Directory's characteristic field, + // if a specific alignment value is needed + if (tlsAlignment) + fixTlsAlignment(); + t1.stop(); if (!config->pdbPath.empty() && config->debug) { @@ -862,6 +869,10 @@ StringRef name = c->getSectionName(); if (shouldStripSectionSuffix(sc, name)) name = name.split('$').first; + + if (name.startswith(".tls")) + tlsAlignment = std::max(tlsAlignment, c->getAlignment()); + PartialSection *pSec = createPartialSection(name, c->getOutputCharacteristics()); pSec->chunks.push_back(c); @@ -2004,3 +2015,34 @@ return it->second; return nullptr; } + +void Writer::fixTlsAlignment() { + if (Symbol *sym = symtab->findUnderscore("_tls_used")) { + if (Defined *b = dyn_cast(sym)) { + OutputSection *sec = b->getChunk()->getOutputSection(); + assert(sec && b->getRVA() >= sec->getRVA() && + "no output section for _tls_used"); + + uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff(); + uint64_t tlsOffset = b->getRVA() - sec->getRVA(); + uint64_t directorySize = config->is64() + ? sizeof(object::coff_tls_directory64) + : sizeof(object::coff_tls_directory32); + + if (tlsOffset + directorySize > sec->getRawSize()) + fatal("_tls_used is malformed"); + + if (config->is64()) { + object::coff_tls_directory64 *tlsDir = + reinterpret_cast( + &secBuf[tlsOffset]); + tlsDir->setAlignment(tlsAlignment); + } else { + object::coff_tls_directory32 *tlsDir = + reinterpret_cast( + &secBuf[tlsOffset]); + tlsDir->setAlignment(tlsAlignment); + } + } + } +} \ No newline at end of file diff --git a/lld/test/COFF/tls-alignment-32.ll b/lld/test/COFF/tls-alignment-32.ll --- a/lld/test/COFF/tls-alignment-32.ll +++ b/lld/test/COFF/tls-alignment-32.ll @@ -11,7 +11,8 @@ ; RUN: llvm-readobj --coff-tls-directory %t.exe | FileCheck %s ; CHECK: TLSDirectory { -; CHECK: Characteristics [ (0x0) +; CHECK: Characteristics [ (0x600000) +; CHECK-NEXT: IMAGE_SCN_ALIGN_32BYTES (0x600000) target triple = "i686-pc-windows-msvc" diff --git a/lld/test/COFF/tls-alignment-64.ll b/lld/test/COFF/tls-alignment-64.ll --- a/lld/test/COFF/tls-alignment-64.ll +++ b/lld/test/COFF/tls-alignment-64.ll @@ -11,7 +11,8 @@ ; RUN: llvm-readobj --coff-tls-directory %t.exe | FileCheck %s ; CHECK: TLSDirectory { -; CHECK: Characteristics [ (0x0) +; CHECK: Characteristics [ (0x700000) +; CHECK-NEXT: IMAGE_SCN_ALIGN_64BYTES (0x700000) target triple = "x86_64-pc-windows-msvc" diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -576,11 +576,22 @@ uint32_t getAlignment() const { // Bit [20:24] contains section alignment. - uint32_t Shift = (Characteristics & 0x00F00000) >> 20; + uint32_t Shift = (Characteristics & COFF::IMAGE_SCN_ALIGN_MASK) >> 20; if (Shift > 0) return 1U << (Shift - 1); return 0; } + + void setAlignment(uint32_t Align) { + if (!Align) { + Characteristics &= ~COFF::IMAGE_SCN_ALIGN_MASK; + } else { + assert(llvm::isPowerOf2_32(Align) && "alignment is not a power of 2"); + uint32_t P2Align = llvm::Log2_32(Align); + assert(P2Align <= 13 && "invalid alignment requested"); + Characteristics |= (P2Align + 1) << 20; + } + } }; using coff_tls_directory32 = coff_tls_directory;