diff --git a/lld/test/COFF/tls-alignment.ll b/lld/test/COFF/tls-alignment.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/tls-alignment.ll @@ -0,0 +1,38 @@ +; RUN: llc -filetype=obj %s -o %t.obj +; RUN: lld-link %t.obj -entry:main -nodefaultlib -out:%t.exe +; RUN: llvm-readobj --coff-tls-table %t.exe | FileCheck %s + +; CHECK: Characteristics [ (0x0) + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%IMAGE_TLS_DIRECTORY = 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 1 +@_tls_used = global %IMAGE_TLS_DIRECTORY { + 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" + +@_fltused = global i8 zeroinitializer + +@aligned_thread_local = thread_local global [32 x i8] zeroinitializer, align 32 + +define i32 @main() #0 { + store <8 x float> zeroinitializer, <8 x float>* bitcast ([32 x i8]* @aligned_thread_local to <8 x float>*), align 32 + ret i32 0 +} \ No newline at end of file diff --git a/llvm/include/llvm/BinaryFormat/COFF.h b/llvm/include/llvm/BinaryFormat/COFF.h --- a/llvm/include/llvm/BinaryFormat/COFF.h +++ b/llvm/include/llvm/BinaryFormat/COFF.h @@ -311,6 +311,7 @@ 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, 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 @@ -786,6 +786,8 @@ const coff_base_reloc_block_header *BaseRelocEnd; const debug_directory *DebugDirectoryBegin; const debug_directory *DebugDirectoryEnd; + const coff_tls_directory32 *TlsTable32; + const coff_tls_directory64 *TlsTable64; // Either coff_load_configuration32 or coff_load_configuration64. const void *LoadConfig = nullptr; @@ -805,6 +807,7 @@ Error initExportTablePtr(); Error initBaseRelocPtr(); Error initDebugDirectoryPtr(); + Error initTlsTablePtr(); Error initLoadConfigPtr(); public: @@ -976,6 +979,9 @@ return make_range(debug_directory_begin(), debug_directory_end()); } + const coff_tls_directory32 *getTlsTable32() const { return TlsTable32; } + const coff_tls_directory64 *getTlsTable64() const { return TlsTable64; } + const dos_header *getDOSHeader() const { if (!PE32Header && !PE32PlusHeader) return nullptr; diff --git a/llvm/lib/Object/COFFObjectFile.cpp b/llvm/lib/Object/COFFObjectFile.cpp --- a/llvm/lib/Object/COFFObjectFile.cpp +++ b/llvm/lib/Object/COFFObjectFile.cpp @@ -649,6 +649,35 @@ return Error::success(); } +Error COFFObjectFile::initTlsTablePtr() { + // Get the RVA of the TLS table. 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 tableSize = + is64() ? sizeof(coff_tls_directory64) : sizeof(coff_tls_directory32); + + // Check that the size is correct + if (DataEntry->Size != tableSize) + return errorCodeToError(object_error::parse_failed); + + uintptr_t IntPtr = 0; + if (Error E = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr)) + return E; + + if (is64()) + TlsTable64 = reinterpret_cast(IntPtr); + else + TlsTable32 = reinterpret_cast(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); @@ -682,7 +711,8 @@ ImportDirectory(nullptr), DelayImportDirectory(nullptr), NumberOfDelayImportDirectory(0), ExportDirectory(nullptr), BaseRelocHeader(nullptr), BaseRelocEnd(nullptr), - DebugDirectoryBegin(nullptr), DebugDirectoryEnd(nullptr) {} + DebugDirectoryBegin(nullptr), DebugDirectoryEnd(nullptr), + TlsTable32(nullptr), TlsTable64(nullptr) {} Error COFFObjectFile::initialize() { // Check that we at least have enough room for a header. @@ -809,10 +839,14 @@ 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 table. + if (Error E = initTlsTablePtr()) + return E; + if (Error E = initLoadConfigPtr()) return E; diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -89,6 +89,7 @@ void printCOFFDirectives() override; void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; + void printTlsTable() override; void printCOFFResources() override; void printCOFFLoadConfig() override; void printCodeViewDebugInfo() override; @@ -116,6 +117,8 @@ void printBaseOfDataField(const pe32plus_header *Hdr); template void printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables); + template + void printTlsTable(const coff_tls_directory *TlsTable); typedef void (*PrintExtraCB)(raw_ostream &, const uint8_t *); void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize, PrintExtraCB PrintExtra = 0); @@ -2018,3 +2021,26 @@ Writer.flush(); } } + +void COFFDumper::printTlsTable() { + if (Obj->is64()) + printTlsTable(Obj->getTlsTable64()); + else + printTlsTable(Obj->getTlsTable32()); +} + +template +void COFFDumper::printTlsTable(const coff_tls_directory *TlsTable) { + if (!TlsTable) + return; + + DictScope D(W, "TLSTable"); + W.printHex("StartAddressOfRawData", TlsTable->StartAddressOfRawData); + W.printHex("EndAddressOfRawData", TlsTable->EndAddressOfRawData); + W.printHex("AddressOfIndex", TlsTable->AddressOfIndex); + W.printHex("AddressOfCallBacks", TlsTable->AddressOfCallBacks); + W.printHex("SizeOfZeroFill", TlsTable->SizeOfZeroFill); + W.printFlags("Characteristics", TlsTable->Characteristics, + makeArrayRef(ImageSectionCharacteristics), + COFF::SectionCharacteristics(COFF::IMAGE_SCN_ALIGN_MASK)); +} \ No newline at end of file diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -80,6 +80,7 @@ virtual void printCOFFDirectives() { } virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } + virtual void printTlsTable() {} virtual void printCOFFResources() {} virtual void printCOFFLoadConfig() { } virtual void printCodeViewDebugInfo() { } diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -272,6 +272,10 @@ COFFDebugDirectory("coff-debug-directory", cl::desc("Display the PE/COFF debug directory")); + // --coff-tls-table + cl::opt COFFTlsTable("coff-tls-table", + cl::desc("Display the PE/COFF TLS table")); + // --coff-resources cl::opt COFFResources("coff-resources", cl::desc("Display the PE/COFF .rsrc section")); @@ -533,6 +537,8 @@ Dumper->printCOFFBaseReloc(); if (opts::COFFDebugDirectory) Dumper->printCOFFDebugDirectory(); + if (opts::COFFTlsTable) + Dumper->printTlsTable(); if (opts::COFFResources) Dumper->printCOFFResources(); if (opts::COFFLoadConfig)