diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -241,6 +241,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); @@ -267,6 +268,7 @@ DelayLoadContents delayIdata; EdataContents edata; bool setNoSEHCharacteristic = false; + uint32_t tlsAlignment = 0; DebugDirectoryChunk *debugDirectory = nullptr; std::vector> debugRecords; @@ -633,6 +635,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) { @@ -866,6 +873,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); @@ -2038,3 +2049,33 @@ return it->second; return nullptr; } + +void Writer::fixTlsAlignment() { + Defined *tlsSym = + dyn_cast_or_null(symtab->findUnderscore("_tls_used")); + if (!tlsSym) + return; + + OutputSection *sec = tlsSym->getChunk()->getOutputSection(); + assert(sec && tlsSym->getRVA() >= sec->getRVA() && + "no output section for _tls_used"); + + uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff(); + uint64_t tlsOffset = tlsSym->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); + } +} 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 @@ -13,7 +13,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 @@ -13,7 +13,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) { + uint32_t AlignBits = 0; + if (Align) { + assert(llvm::isPowerOf2_32(Align) && "alignment is not a power of 2"); + assert(llvm::Log2_32(Align) <= 13 && "alignment requested is too large"); + AlignBits = (llvm::Log2_32(Align) + 1) << 20; + } + Characteristics = + (Characteristics & ~COFF::IMAGE_SCN_ALIGN_MASK) | AlignBits; + } }; using coff_tls_directory32 = coff_tls_directory;