diff --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst --- a/llvm/docs/CommandGuide/llvm-readobj.rst +++ b/llvm/docs/CommandGuide/llvm-readobj.rst @@ -286,6 +286,10 @@ 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 --- 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 *TLSDirectory32; + const coff_tls_directory64 *TLSDirectory64; // Either coff_load_configuration32 or coff_load_configuration64. const void *LoadConfig = nullptr; @@ -805,6 +807,7 @@ Error initExportTablePtr(); Error initBaseRelocPtr(); Error initDebugDirectoryPtr(); + Error initTLSDirectoryPtr(); Error initLoadConfigPtr(); public: @@ -976,6 +979,13 @@ 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 --- a/llvm/lib/Object/COFFObjectFile.cpp +++ b/llvm/lib/Object/COFFObjectFile.cpp @@ -649,6 +649,38 @@ 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 @@ 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 @@ 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 --- /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 --- 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 printCOFFTLSDirectory() 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 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); @@ -2018,3 +2021,27 @@ 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 --- 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 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 --- 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-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")); @@ -533,6 +537,8 @@ Dumper->printCOFFBaseReloc(); if (opts::COFFDebugDirectory) Dumper->printCOFFDebugDirectory(); + if (opts::COFFTLSDirectory) + Dumper->printCOFFTLSDirectory(); if (opts::COFFResources) Dumper->printCOFFResources(); if (opts::COFFLoadConfig)