From 58a2d71339e3f8e979992da68fe0bcfa1df14f9f Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Thu, 8 Oct 2020 01:43:50 -0700 Subject: [PATCH 1/3] [llvm-readobj] Add --coff-tls-directory flag to print TLS Directory & test. Akin to dumpbin's /TLS option, this will print out the TLS directory, if present, in the image. Example output: ``` > llvm-readobj --coff-tls-directory test.exe File: test.exe Format: COFF-x86-64 Arch: x86_64 AddressSize: 64bit TLSDirectory { StartAddressOfRawData: 0x140004000 EndAddressOfRawData: 0x140004040 AddressOfIndex: 0x140002000 AddressOfCallBacks: 0x0 SizeOfZeroFill: 0x0 Characteristics [ (0x0) ] } ``` Reviewed By: jhenderson, grimar Differential Revision: https://reviews.llvm.org/D88635 --- llvm/docs/CommandGuide/llvm-readobj.rst | 4 + llvm/include/llvm/BinaryFormat/COFF.h | 1 + llvm/include/llvm/Object/COFF.h | 10 ++ llvm/lib/Object/COFFObjectFile.cpp | 41 ++++- .../llvm-readobj/COFF/tls-directory.test | 162 ++++++++++++++++++ llvm/tools/llvm-readobj/COFFDumper.cpp | 27 +++ llvm/tools/llvm-readobj/ObjDumper.h | 1 + llvm/tools/llvm-readobj/llvm-readobj.cpp | 6 + 8 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 llvm/test/tools/llvm-readobj/COFF/tls-directory.test diff --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst index 51cd266ff79e0..5032163add085 100644 --- a/llvm/docs/CommandGuide/llvm-readobj.rst +++ b/llvm/docs/CommandGuide/llvm-readobj.rst @@ -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. diff --git a/llvm/include/llvm/BinaryFormat/COFF.h b/llvm/include/llvm/BinaryFormat/COFF.h index 1919d7f0dece9..716d649f7c51c 100644 --- a/llvm/include/llvm/BinaryFormat/COFF.h +++ b/llvm/include/llvm/BinaryFormat/COFF.h @@ -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, diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h index 8aef00a8809dc..505aab8bff5b3 100644 --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -786,6 +786,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; @@ -805,6 +807,7 @@ class COFFObjectFile : public ObjectFile { Error initExportTablePtr(); Error initBaseRelocPtr(); Error initDebugDirectoryPtr(); + Error initTLSDirectoryPtr(); Error initLoadConfigPtr(); public: @@ -976,6 +979,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; diff --git a/llvm/lib/Object/COFFObjectFile.cpp b/llvm/lib/Object/COFFObjectFile.cpp index c26d7721b3fe9..cd10e67af239e 100644 --- a/llvm/lib/Object/COFFObjectFile.cpp +++ b/llvm/lib/Object/COFFObjectFile.cpp @@ -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(DataEntry->Size), DirSize); + + uintptr_t IntPtr = 0; + if (Error E = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr)) + return E; + + if (is64()) + TLSDirectory64 = reinterpret_cast(IntPtr); + else + TLSDirectory32 = 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 +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. @@ -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; diff --git a/llvm/test/tools/llvm-readobj/COFF/tls-directory.test b/llvm/test/tools/llvm-readobj/COFF/tls-directory.test new file mode 100644 index 0000000000000..d553130e0a017 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/COFF/tls-directory.test @@ -0,0 +1,162 @@ +## Tests for the --coff-tls-directory flag. + +## Test that the output of --coff-tls-directory works on x86. +## The binary created from this yaml definition is such that .rdata contains +## only the IMAGE_TLS_DIRECTORY structure and hence we should have that +## TlsTable.RelativeVirtualAddress == .rdata section VirtualAddress. +## Also note that the .rdata section VirtualSize == sizeof(coff_tls_directory32) == sizeof(IMAGE_TLS_DIRECTORY32) == 24 + +# RUN: yaml2obj %s --docnum=1 -o %t.32.exe -DTLSRVA=10000 -DTLSSIZE=24 +# RUN: llvm-readobj --coff-tls-directory %t.32.exe | FileCheck %s --check-prefix I386 + +# I386: Arch: i386 +# I386-NEXT: AddressSize: 32bit +# I386-NEXT: TLSDirectory { +# I386-NEXT: StartAddressOfRawData: 0x404000 +# I386-NEXT: EndAddressOfRawData: 0x404008 +# I386-NEXT: AddressOfIndex: 0x402000 +# I386-NEXT: AddressOfCallBacks: 0x0 +# I386-NEXT: SizeOfZeroFill: 0x0 +# I386-NEXT: Characteristics [ (0x300000) +# I386-NEXT: IMAGE_SCN_ALIGN_4BYTES (0x300000) +# I386-NEXT: ] +# I386-NEXT: } + + +## Test that the output of --coff-tls-directory errors on malformed input. +## On x86, the TLS directory should be 24 bytes. +## This test has a truncated TLS directory. + +# RUN: yaml2obj %s --docnum=1 -o %t.wrong-size.32.exe -DTLSRVA=10000 -DTLSSIZE=10 +# RUN: not llvm-readobj --coff-tls-directory %t.wrong-size.32.exe 2>&1 | FileCheck %s --check-prefix I386-WRONG-SIZE-ERR + +# I386-WRONG-SIZE-ERR: error: '{{.*}}': TLS Directory size (10) is not the expected size (24). + +--- !COFF +OptionalHeader: + AddressOfEntryPoint: 0 + ImageBase: 0 + SectionAlignment: 4096 + FileAlignment: 512 + MajorOperatingSystemVersion: 0 + MinorOperatingSystemVersion: 0 + MajorImageVersion: 0 + MinorImageVersion: 0 + MajorSubsystemVersion: 0 + MinorSubsystemVersion: 0 + Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI + DLLCharacteristics: [] + SizeOfStackReserve: 0 + SizeOfStackCommit: 0 + SizeOfHeapReserve: 0 + SizeOfHeapCommit: 0 + TlsTable: + RelativeVirtualAddress: [[TLSRVA]] + Size: [[TLSSIZE]] +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_32BIT_MACHINE ] +sections: + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + VirtualAddress: 10000 + VirtualSize: 24 + SectionData: '004040000840400000204000000000000000000000003000' +symbols: [] + + +## Test that the output of --coff-tls-directory works on x86_64. +## The binary created from this yaml definition is such that .rdata contains +## only the IMAGE_TLS_DIRECTORY structure and hence we should have that +## TlsTable.RelativeVirtualAddress == .rdata section VirtualAddress. +## Also note that the .rdata section VirtualSize == sizeof(coff_tls_directory64) == sizeof(IMAGE_TLS_DIRECTORY64) == 40 + +# RUN: yaml2obj %s --docnum=2 -o %t.64.exe -DTLSRVA=10000 -DTLSSIZE=40 +# RUN: llvm-readobj --coff-tls-directory %t.64.exe | FileCheck %s --check-prefix X86-64 + +# X86-64: Arch: x86_64 +# X86-64-NEXT: AddressSize: 64bit +# X86-64-NEXT: TLSDirectory { +# X86-64-NEXT: StartAddressOfRawData: 0x140004000 +# X86-64-NEXT: EndAddressOfRawData: 0x140004008 +# X86-64-NEXT: AddressOfIndex: 0x140002000 +# X86-64-NEXT: AddressOfCallBacks: 0x0 +# X86-64-NEXT: SizeOfZeroFill: 0x0 +# X86-64-NEXT: Characteristics [ (0x300000) +# X86-64-NEXT: IMAGE_SCN_ALIGN_4BYTES (0x300000) +# X86-64-NEXT: ] +# X86-64-NEXT: } + + +## Test that the output of --coff-tls-directory errors on malformed input. + +## On x86-64, the TLS directory should be 40 bytes. +## This test has an erroneously lengthened TLS directory. + +# RUN: yaml2obj %s --docnum=2 -o %t.wrong-size.64.exe -DTLSRVA=10000 -DTLSSIZE=80 +# RUN: not llvm-readobj --coff-tls-directory %t.wrong-size.64.exe 2>&1 | FileCheck %s --check-prefix X86-64-WRONG-SIZE-ERR + +# X86-64-WRONG-SIZE-ERR: error: '{{.*}}': TLS Directory size (80) is not the expected size (40). + + +## This test has a correct TLS Directory size but the RVA is invalid. + +# RUN: yaml2obj %s --docnum=2 -o %t.bad-tls-rva.exe -DTLSRVA=999999 -DTLSSIZE=40 +# RUN: not llvm-readobj --coff-tls-directory %t.bad-tls-rva.exe 2>&1 | FileCheck %s --check-prefix BAD-TLS-RVA-ERR + +# BAD-TLS-RVA-ERR: error: '{{.*}}': Invalid data was encountered while parsing the file + +--- !COFF +OptionalHeader: + AddressOfEntryPoint: 0 + ImageBase: 0 + SectionAlignment: 4096 + FileAlignment: 512 + MajorOperatingSystemVersion: 0 + MinorOperatingSystemVersion: 0 + MajorImageVersion: 0 + MinorImageVersion: 0 + MajorSubsystemVersion: 0 + MinorSubsystemVersion: 0 + Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI + DLLCharacteristics: [] + SizeOfStackReserve: 0 + SizeOfStackCommit: 0 + SizeOfHeapReserve: 0 + SizeOfHeapCommit: 0 + TlsTable: + RelativeVirtualAddress: [[TLSRVA]] + Size: [[TLSSIZE]] +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ] +sections: + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + VirtualAddress: 10000 + VirtualSize: 40 + SectionData: '00400040010000000840004001000000002000400100000000000000000000000000000000003000' +symbols: [] + + +## Test that --coff-tls-directory doesn't output anything if there's no TLS directory. + +## Case 1: TlsTable.RelativeVirtualAddress/Size = 0. + +# RUN: yaml2obj %s --docnum=2 -o %t.no-tls1.exe -DTLSRVA=0 -DTLSSIZE=0 +# RUN: llvm-readobj --coff-tls-directory %t.no-tls1.exe | FileCheck %s --check-prefix NO-TLS + +## Case 2: There's no TlsTable listed in the COFF header. + +# RUN: yaml2obj %s --docnum=3 -o %t.no-tls2.exe +# RUN: llvm-readobj --coff-tls-directory %t.no-tls2.exe | FileCheck %s --check-prefix NO-TLS + +# NO-TLS: TLSDirectory { +# NO-TLS-NEXT: } + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ] +sections: [] +symbols: [] diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp index 89a904f53ae7d..57162fd38a541 100644 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -94,6 +94,7 @@ class COFFDumper : public ObjDumper { void printCOFFDirectives() override; void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; + void printCOFFTLSDirectory() override; void printCOFFResources() override; void printCOFFLoadConfig() override; void printCodeViewDebugInfo() override; @@ -121,6 +122,8 @@ class COFFDumper : public ObjDumper { void printBaseOfDataField(const pe32plus_header *Hdr); template void printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables); + template + void printCOFFTLSDirectory(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); @@ -2029,3 +2032,27 @@ void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, Writer.flush(); } } + +void COFFDumper::printCOFFTLSDirectory() { + if (Obj->is64()) + printCOFFTLSDirectory(Obj->getTLSDirectory64()); + else + printCOFFTLSDirectory(Obj->getTLSDirectory32()); +} + +template +void COFFDumper::printCOFFTLSDirectory( + const coff_tls_directory *TlsTable) { + DictScope D(W, "TLSDirectory"); + if (!TlsTable) + return; + + 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)); +} diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index 57477606d6e8e..72f8497c8299f 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -78,6 +78,7 @@ class ObjDumper { virtual void printCOFFDirectives() { } virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } + virtual void printCOFFTLSDirectory() {} 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 index b9c6ad2256ae2..4d1a494eeb8f3 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -265,6 +265,10 @@ namespace opts { COFFDebugDirectory("coff-debug-directory", cl::desc("Display the PE/COFF debug directory")); + // --coff-tls-directory + cl::opt COFFTLSDirectory("coff-tls-directory", + cl::desc("Display the PE/COFF TLS directory")); + // --coff-resources cl::opt COFFResources("coff-resources", cl::desc("Display the PE/COFF .rsrc section")); @@ -524,6 +528,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, Dumper->printCOFFBaseReloc(); if (opts::COFFDebugDirectory) Dumper->printCOFFDebugDirectory(); + if (opts::COFFTLSDirectory) + Dumper->printCOFFTLSDirectory(); if (opts::COFFResources) Dumper->printCOFFResources(); if (opts::COFFLoadConfig) From 5819d9e3c95408704889a7bde3e54b600ccf2477 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Tue, 6 Oct 2020 19:16:34 -0700 Subject: [PATCH 2/3] [LLD] Add baseline test for TLS alignment. NFC. Reviewed By: rnk Differential Revision: https://reviews.llvm.org/D88646 --- lld/test/COFF/Inputs/tlssup-32.ll | 31 +++++++++++++++++++++++++++++++ lld/test/COFF/Inputs/tlssup-64.ll | 26 ++++++++++++++++++++++++++ lld/test/COFF/tls-alignment-32.ll | 23 +++++++++++++++++++++++ lld/test/COFF/tls-alignment-64.ll | 23 +++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 lld/test/COFF/Inputs/tlssup-32.ll create mode 100644 lld/test/COFF/Inputs/tlssup-64.ll create mode 100644 lld/test/COFF/tls-alignment-32.ll create mode 100644 lld/test/COFF/tls-alignment-64.ll diff --git a/lld/test/COFF/Inputs/tlssup-32.ll b/lld/test/COFF/Inputs/tlssup-32.ll new file mode 100644 index 0000000000000..e49cde4969e51 --- /dev/null +++ b/lld/test/COFF/Inputs/tlssup-32.ll @@ -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" \ No newline at end of file diff --git a/lld/test/COFF/Inputs/tlssup-64.ll b/lld/test/COFF/Inputs/tlssup-64.ll new file mode 100644 index 0000000000000..aadcf989a2def --- /dev/null +++ b/lld/test/COFF/Inputs/tlssup-64.ll @@ -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" \ No newline at end of file diff --git a/lld/test/COFF/tls-alignment-32.ll b/lld/test/COFF/tls-alignment-32.ll new file mode 100644 index 0000000000000..fe8a3e0c8661d --- /dev/null +++ b/lld/test/COFF/tls-alignment-32.ll @@ -0,0 +1,23 @@ +; 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 [ (0x0) + +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 +} diff --git a/lld/test/COFF/tls-alignment-64.ll b/lld/test/COFF/tls-alignment-64.ll new file mode 100644 index 0000000000000..495678d01fbb0 --- /dev/null +++ b/lld/test/COFF/tls-alignment-64.ll @@ -0,0 +1,23 @@ +; 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 [ (0x0) + +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 +} From c9d28e5c9256ae4fef4fc55db1d09afebff7f5fb Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Thu, 15 Oct 2020 00:06:46 -0700 Subject: [PATCH 3/3] [LLD] Set alignment as part of Characteristics in TLS table. Fixes https://bugs.llvm.org/show_bug.cgi?id=46473 LLD wasn't previously specifying any specific alignment in the TLS table's Characteristics field so the loader would just assume the default value (16 bytes). This works most of the time except if you have thread locals that want specific higher alignments (e.g. 32 as in the bug) *even* if they specify an alignment on the thread local. This change updates LLD to take the max alignment from tls section. Reviewed By: rnk Differential Revision: https://reviews.llvm.org/D88637 --- lld/COFF/Writer.cpp | 41 +++++++++++++++++++++++++++++++ lld/test/COFF/tls-alignment-32.ll | 3 ++- lld/test/COFF/tls-alignment-64.ll | 3 ++- llvm/include/llvm/Object/COFF.h | 13 +++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 0188f0971a754..c7d4de7006054 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -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); @@ -260,6 +261,7 @@ class Writer { DelayLoadContents delayIdata; EdataContents edata; bool setNoSEHCharacteristic = false; + uint32_t tlsAlignment = 0; DebugDirectoryChunk *debugDirectory = nullptr; std::vector> debugRecords; @@ -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) { @@ -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); @@ -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(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(&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 index fe8a3e0c8661d..0f12498afcfc2 100644 --- 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 index 495678d01fbb0..4c5c691b57131 100644 --- 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 index 505aab8bff5b3..e7cf1b5495c66 100644 --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -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;