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,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/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;