diff --git a/compiler-rt/lib/orc/CMakeLists.txt b/compiler-rt/lib/orc/CMakeLists.txt index b8d1b03b788c9..649d988d9d608 100644 --- a/compiler-rt/lib/orc/CMakeLists.txt +++ b/compiler-rt/lib/orc/CMakeLists.txt @@ -119,6 +119,7 @@ else() # not Apple elfnix_tls.x86-64.S elfnix_tls.aarch64.S elfnix_tls.ppc64.S + elfnix_tls.systemz.S sysv_reenter.arm64.S sysv_reenter.x86-64.S ) diff --git a/compiler-rt/lib/orc/elfnix_tls.systemz.S b/compiler-rt/lib/orc/elfnix_tls.systemz.S new file mode 100644 index 0000000000000..4e116c92a5a88 --- /dev/null +++ b/compiler-rt/lib/orc/elfnix_tls.systemz.S @@ -0,0 +1,42 @@ +//===-- orc_rt_elfnix_tls_systemz.s -------------------------------*- ASM -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the ORC runtime support library. +// +//===----------------------------------------------------------------------===// + +// The special thing about the s390 TLS ABI is that we do not have the +// standard __tls_get_addr function but the __tls_get_offset function +// which differs in two important aspects: +// 1) __tls_get_offset gets a got offset instead of a pointer to the +// tls_index structure +// 2) __tls_get_offset returns the offset of the requested variable to +// the thread descriptor instead of a pointer to the variable. + +// The content of this file is systemz-only + +#if defined(__s390x__) + + .text + // returns offset of TLV from TP in %r2. + .globl ___orc_rt_elfnix_tls_get_offset +___orc_rt_elfnix_tls_get_offset: + stmg %r14, %r15, 112(%r15) + aghi %r15, -160 + // Pass pointer to tls_index. + la %r2, 0(%r2, %r12) + brasl %r14, __orc_rt_elfnix_tls_get_addr_impl + // Return offset from TP. + ear %r0, %a0 + sllg %r0, %r0, 32 + ear %r0, %a1 + sgr %r2, %r0 + lmg %r14, %r15, 272(%r15) + br %r14 + +#endif // defined(__s390x__) diff --git a/compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S b/compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S new file mode 100644 index 0000000000000..4bf1c578bd1d7 --- /dev/null +++ b/compiler-rt/test/orc/TestCases/Linux/systemz/trivial-tls.S @@ -0,0 +1,67 @@ +// RUN: %clang -c -o %t %s +// RUN: %llvm_jitlink %t +// +// Test that basic ELF TLS work by adding together TLSs with values +// 0, 1, and -1, and returning the result (0 for success). This setup +// tests both zero-initialized (.tbss) and non-zero-initialized +// (.tdata) sections. + + .section .data.rel.ro,"aw",@progbits + .p2align 3, 0x0 +.LCPI0_0: + .quad x@TLSGD +.LCPI0_1: + .quad y@TLSGD +.LCPI0_2: + .quad z@TLSGD + + .text + .globl main + .p2align 4 + .type main,@function +main: + stmg %r10, %r15, 80(%r15) + aghi %r15, -160 + lgrl %r2, .LCPI0_0 + larl %r12, _GLOBAL_OFFSET_TABLE_ + brasl %r14, __tls_get_offset@PLT:tls_gdcall:x + lgr %r13, %r2 + lgrl %r2, .LCPI0_1 + brasl %r14, __tls_get_offset@PLT:tls_gdcall:y + ear %r0, %a0 + sllg %r11, %r0, 32 + ear %r11, %a1 + l %r10, 0(%r2,%r11) + lgrl %r2, .LCPI0_2 + a %r10, 0(%r13,%r11) + brasl %r14, __tls_get_offset@PLT:tls_gdcall:z + a %r10, 0(%r2,%r11) + lgfr %r2, %r10 + lmg %r10, %r15, 240(%r15) + br %r14 +.Lfunc_end0: + .size main, .Lfunc_end0-main + + + .type x,@object # @x + .section .tbss,"awT",@nobits + .globl x + .p2align 2, 0x0 +x: + .long 0 # 0x0 + .size x, 4 + + .type y,@object # @y + .section .tdata,"awT",@progbits + .globl y + .p2align 2, 0x0 +y: + .long 1 # 0x1 + .size y, 4 + + .type z,@object # @z + .globl z + .p2align 2, 0x0 +z: + .long 4294967295 # 0xffffffff + .size z, 4 diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h b/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h index 09ec56db6826f..bfd22ec753074 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/systemz.h @@ -507,6 +507,21 @@ enum EdgeKind_systemz : Edge::Kind { /// RequestGOTAndTransformToDelta32dbl, + /// A TLSInfo entry getter/constructor, transformed to Delta64FromGOT. + /// + /// Indicates that this edge should be transformed into a Delta64FromGOT + /// targeting the TLSInfo entry for the edge's current target. A TLSInfo + /// entry for the target should be created if one does not already exist. + /// + /// Fixup expression: + /// NONE + /// + /// Errors: + /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup + /// phase will result in an assert/unreachable during the fixup phase. + /// + RequestTLSDescInGOTAndTransformToDelta64FromGOT, + /// A 32-bit Delta to GOT base. /// /// Fixup expression: diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp index 29eeecceea766..50acd6ea2e542 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_systemz.cpp @@ -27,12 +27,67 @@ using namespace llvm::jitlink; namespace { constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_"; +constexpr StringRef ELFTLSInfoSectionName = "$__TLSINFO"; + +// TLS Info Builder. +class TLSInfoTableManager_ELF_systemz + : public TableManager { +public: + static StringRef getSectionName() { return ELFTLSInfoSectionName; } + + static const uint8_t TLSInfoEntryContent[16]; + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + if (E.getKind() == + systemz::RequestTLSDescInGOTAndTransformToDelta64FromGOT) { + LLVM_DEBUG({ + dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " + << formatv("{0:x}", B->getFixupAddress(E)) << " (" + << formatv("{0:x}", B->getAddress()) << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"; + }); + E.setKind(systemz::Delta64FromGOT); + E.setTarget(getEntryForTarget(G, E.getTarget())); + return true; + } + return false; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + // the TLS Info entry's key value will be written by the fixTLVSectionByName + // pass, so create mutable content. + auto &TLSInfoEntry = G.createMutableContentBlock( + getTLSInfoSection(G), G.allocateContent(getTLSInfoEntryContent()), + orc::ExecutorAddr(), 8, 0); + TLSInfoEntry.addEdge(systemz::Pointer64, 8, Target, 0); + return G.addAnonymousSymbol(TLSInfoEntry, 0, 16, false, false); + } + +private: + Section &getTLSInfoSection(LinkGraph &G) { + if (!TLSInfoTable) + TLSInfoTable = &G.createSection(getSectionName(), orc::MemProt::Read); + return *TLSInfoTable; + } + + ArrayRef getTLSInfoEntryContent() const { + return {reinterpret_cast(TLSInfoEntryContent), + sizeof(TLSInfoEntryContent)}; + } + + Section *TLSInfoTable = nullptr; +}; + +const uint8_t TLSInfoTableManager_ELF_systemz::TLSInfoEntryContent[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; Error buildTables_ELF_systemz(LinkGraph &G) { LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); systemz::GOTTableManager GOT; systemz::PLTTableManager PLT(GOT); - visitExistingEdges(G, GOT, PLT); + TLSInfoTableManager_ELF_systemz TLSInfo; + visitExistingEdges(G, GOT, PLT, TLSInfo); return Error::success(); } @@ -329,6 +384,15 @@ class ELFLinkGraphBuilder_systemz Kind = systemz::Delta32dblGOTBase; break; } + // Tag for function call in general dynamic TLS code. + case ELF::R_390_TLS_GDCALL: { + break; + } + // Direct 64 bit for general dynamic thread local data. + case ELF::R_390_TLS_GD64: { + Kind = systemz::RequestTLSDescInGOTAndTransformToDelta64FromGOT; + break; + } default: return make_error( "In " + G->getName() + ": Unsupported systemz relocation type " + diff --git a/llvm/lib/ExecutionEngine/JITLink/systemz.cpp b/llvm/lib/ExecutionEngine/JITLink/systemz.cpp index f6cc29fa6e6a1..dbb924c3f9291 100644 --- a/llvm/lib/ExecutionEngine/JITLink/systemz.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/systemz.cpp @@ -104,6 +104,8 @@ const char *getEdgeKindName(Edge::Kind R) { return "RequestGOTAndTransformToDelta12FromGOT"; case RequestGOTAndTransformToDelta32dbl: return "RequestGOTAndTransformToDelta32dbl"; + case RequestTLSDescInGOTAndTransformToDelta64FromGOT: + return "RequestTLSDescInGOTAndTransformToDelta64FromGOT"; default: return getGenericEdgeKindName(static_cast(R)); } diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp index 7dc1ae520f132..0a761290373aa 100644 --- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp @@ -988,6 +988,7 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges( jitlink::LinkGraph &G, JITDylib &JD) { auto TLSGetAddrSymbolName = G.intern("__tls_get_addr"); auto TLSDescResolveSymbolName = G.intern("__tlsdesc_resolver"); + auto TLSGetOffsetSymbolName = G.intern("__tls_get_offset"); for (auto *Sym : G.external_symbols()) { if (Sym->getName() == TLSGetAddrSymbolName) { auto TLSGetAddr = @@ -997,6 +998,10 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges( auto TLSGetAddr = MP.getExecutionSession().intern("___orc_rt_elfnix_tlsdesc_resolver"); Sym->setName(std::move(TLSGetAddr)); + } else if (Sym->getName() == TLSGetOffsetSymbolName) { + auto TLSGetAddr = + MP.getExecutionSession().intern("___orc_rt_elfnix_tls_get_offset"); + Sym->setName(std::move(TLSGetAddr)); } }