Skip to content

[LLD] Set alignment as part of Characteristics in TLS table. #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions lld/COFF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class Writer {
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);

Expand All @@ -260,6 +261,7 @@ class Writer {
DelayLoadContents delayIdata;
EdataContents edata;
bool setNoSEHCharacteristic = false;
uint32_t tlsAlignment = 0;

DebugDirectoryChunk *debugDirectory = nullptr;
std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
Expand Down Expand Up @@ -628,6 +630,11 @@ void Writer::run() {
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) {
Expand Down Expand Up @@ -848,6 +855,10 @@ void Writer::createSections() {
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);
Expand Down Expand Up @@ -1993,3 +2004,33 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
return it->second;
return nullptr;
}

void Writer::fixTlsAlignment() {
Defined *tlsSym =
dyn_cast_or_null<Defined>(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 sym is malformed");

if (config->is64()) {
object::coff_tls_directory64 *tlsDir =
reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
tlsDir->setAlignment(tlsAlignment);
} else {
object::coff_tls_directory32 *tlsDir =
reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]);
tlsDir->setAlignment(tlsAlignment);
}
}
31 changes: 31 additions & 0 deletions lld/test/COFF/Inputs/tlssup-32.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; We manually create these here if we're not linking against
; the CRT which would usually provide these.

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

%IMAGE_TLS_DIRECTORY32 = type {
i32, ; StartAddressOfRawData
i32, ; EndAddressOfRawData
i32, ; AddressOfIndex
i32, ; AddressOfCallBacks
i32, ; SizeOfZeroFill
i32 ; Characteristics
}

@_tls_start = global i8 zeroinitializer, section ".tls"
@_tls_end = global i8 zeroinitializer, section ".tls$ZZZ"
@_tls_index = global i32 0

@_tls_used = global %IMAGE_TLS_DIRECTORY32 {
i32 ptrtoint (i8* @_tls_start to i32),
i32 ptrtoint (i8* @_tls_end to i32),
i32 ptrtoint (i32* @_tls_index to i32),
i32 0,
i32 0,
i32 0
}, section ".rdata$T"

; MSVC target uses a direct offset (0x58) for x86-64 but expects
; __tls_array to hold the offset (0x2C) on x86.
module asm ".global __tls_array"
module asm "__tls_array = 44"
26 changes: 26 additions & 0 deletions lld/test/COFF/Inputs/tlssup-64.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
; We manually create these here if we're not linking against
; the CRT which would usually provide these.

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

%IMAGE_TLS_DIRECTORY64 = type {
i64, ; StartAddressOfRawData
i64, ; EndAddressOfRawData
i64, ; AddressOfIndex
i64, ; AddressOfCallBacks
i32, ; SizeOfZeroFill
i32 ; Characteristics
}

@_tls_start = global i8 zeroinitializer, section ".tls"
@_tls_end = global i8 zeroinitializer, section ".tls$ZZZ"
@_tls_index = global i64 0

@_tls_used = global %IMAGE_TLS_DIRECTORY64 {
i64 ptrtoint (i8* @_tls_start to i64),
i64 ptrtoint (i8* @_tls_end to i64),
i64 ptrtoint (i64* @_tls_index to i64),
i64 0,
i32 0,
i32 0
}, section ".rdata$T"
24 changes: 24 additions & 0 deletions lld/test/COFF/tls-alignment-32.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; This test is to make sure that the necessary alignment for thread locals
; gets reflected in the TLS Directory of the generated executable on x86.
;
; aligned_thread_local specifies 'align 32' and so the generated
; exe should reflect that with a value of IMAGE_SCN_ALIGN_32BYTES
; in the Characteristics field of the IMAGE_TLS_DIRECTORY

; RUN: llc -filetype=obj %S/Inputs/tlssup-32.ll -o %t.tlssup.obj
; RUN: llc -filetype=obj %s -o %t.obj
; RUN: lld-link %t.tlssup.obj %t.obj -entry:main -nodefaultlib -out:%t.exe
; RUN: llvm-readobj --coff-tls-directory %t.exe | FileCheck %s

; CHECK: TLSDirectory {
; CHECK: Characteristics [ (0x600000)
; CHECK-NEXT: IMAGE_SCN_ALIGN_32BYTES (0x600000)

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

@aligned_thread_local = thread_local global i32 42, align 32

define i32 @main() {
%t = load i32, i32* @aligned_thread_local
ret i32 %t
}
24 changes: 24 additions & 0 deletions lld/test/COFF/tls-alignment-64.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; This test is to make sure that the necessary alignment for thread locals
; gets reflected in the TLS Directory of the generated executable on x86-64.
;
; aligned_thread_local specifies 'align 64' and so the generated
; exe should reflect that with a value of IMAGE_SCN_ALIGN_64BYTES
; in the Characteristics field of the IMAGE_TLS_DIRECTORY

; RUN: llc -filetype=obj %S/Inputs/tlssup-64.ll -o %t.tlssup.obj
; RUN: llc -filetype=obj %s -o %t.obj
; RUN: lld-link %t.tlssup.obj %t.obj -entry:main -nodefaultlib -out:%t.exe
; RUN: llvm-readobj --coff-tls-directory %t.exe | FileCheck %s

; CHECK: TLSDirectory {
; CHECK: Characteristics [ (0x700000)
; CHECK-NEXT: IMAGE_SCN_ALIGN_64BYTES (0x700000)

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

@aligned_thread_local = thread_local global i32 42, align 64

define i32 @main() {
%t = load i32, i32* @aligned_thread_local
ret i32 %t
}
4 changes: 4 additions & 0 deletions llvm/docs/CommandGuide/llvm-readobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ The following options are implemented only for the PE/COFF file format.

Display the debug directory.

.. option:: --coff-tls-directory

Display the TLS directory.

.. option:: --coff-directives

Display the .drectve section.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/COFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ enum SectionCharacteristics : uint32_t {
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000,
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000,
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000,
IMAGE_SCN_ALIGN_MASK = 0x00F00000,
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000,
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000,
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000,
Expand Down
23 changes: 22 additions & 1 deletion llvm/include/llvm/Object/COFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,11 +576,22 @@ struct coff_tls_directory {

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<support::little32_t>;
Expand Down Expand Up @@ -786,6 +797,8 @@ class COFFObjectFile : public ObjectFile {
const coff_base_reloc_block_header *BaseRelocEnd;
const debug_directory *DebugDirectoryBegin;
const debug_directory *DebugDirectoryEnd;
const coff_tls_directory32 *TLSDirectory32;
const coff_tls_directory64 *TLSDirectory64;
// Either coff_load_configuration32 or coff_load_configuration64.
const void *LoadConfig = nullptr;

Expand All @@ -805,6 +818,7 @@ class COFFObjectFile : public ObjectFile {
Error initExportTablePtr();
Error initBaseRelocPtr();
Error initDebugDirectoryPtr();
Error initTLSDirectoryPtr();
Error initLoadConfigPtr();

public:
Expand Down Expand Up @@ -976,6 +990,13 @@ class COFFObjectFile : public ObjectFile {
return make_range(debug_directory_begin(), debug_directory_end());
}

const coff_tls_directory32 *getTLSDirectory32() const {
return TLSDirectory32;
}
const coff_tls_directory64 *getTLSDirectory64() const {
return TLSDirectory64;
}

const dos_header *getDOSHeader() const {
if (!PE32Header && !PE32PlusHeader)
return nullptr;
Expand Down
41 changes: 39 additions & 2 deletions llvm/lib/Object/COFFObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,38 @@ Error COFFObjectFile::initDebugDirectoryPtr() {
return Error::success();
}

Error COFFObjectFile::initTLSDirectoryPtr() {
// Get the RVA of the TLS directory. Do nothing if it does not exist.
const data_directory *DataEntry = getDataDirectory(COFF::TLS_TABLE);
if (!DataEntry)
return Error::success();

// Do nothing if the RVA is NULL.
if (DataEntry->RelativeVirtualAddress == 0)
return Error::success();

uint64_t DirSize =
is64() ? sizeof(coff_tls_directory64) : sizeof(coff_tls_directory32);

// Check that the size is correct.
if (DataEntry->Size != DirSize)
return createStringError(
object_error::parse_failed,
"TLS Directory size (%u) is not the expected size (%u).",
static_cast<uint32_t>(DataEntry->Size), DirSize);

uintptr_t IntPtr = 0;
if (Error E = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr))
return E;

if (is64())
TLSDirectory64 = reinterpret_cast<const coff_tls_directory64 *>(IntPtr);
else
TLSDirectory32 = reinterpret_cast<const coff_tls_directory32 *>(IntPtr);

return Error::success();
}

Error COFFObjectFile::initLoadConfigPtr() {
// Get the RVA of the debug directory. Do nothing if it does not exist.
const data_directory *DataEntry = getDataDirectory(COFF::LOAD_CONFIG_TABLE);
Expand Down Expand Up @@ -682,7 +714,8 @@ COFFObjectFile::COFFObjectFile(MemoryBufferRef Object)
ImportDirectory(nullptr), DelayImportDirectory(nullptr),
NumberOfDelayImportDirectory(0), ExportDirectory(nullptr),
BaseRelocHeader(nullptr), BaseRelocEnd(nullptr),
DebugDirectoryBegin(nullptr), DebugDirectoryEnd(nullptr) {}
DebugDirectoryBegin(nullptr), DebugDirectoryEnd(nullptr),
TLSDirectory32(nullptr), TLSDirectory64(nullptr) {}

Error COFFObjectFile::initialize() {
// Check that we at least have enough room for a header.
Expand Down Expand Up @@ -809,10 +842,14 @@ Error COFFObjectFile::initialize() {
if (Error E = initBaseRelocPtr())
return E;

// Initialize the pointer to the export table.
// Initialize the pointer to the debug directory.
if (Error E = initDebugDirectoryPtr())
return E;

// Initialize the pointer to the TLS directory.
if (Error E = initTLSDirectoryPtr())
return E;

if (Error E = initLoadConfigPtr())
return E;

Expand Down
Loading