From e20e0134d2047b4c93f649aec9fdc4208d9d8af4 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Fri, 15 Dec 2023 20:04:03 +0100 Subject: [PATCH] [lld] Add target support for SystemZ (s390x) This patch adds full support for linking SystemZ (ELF s390x) object files. Support should be generally complete: - All relocation types are supported. - Full shared library support (DYNAMIC, GOT, PLT, ifunc). - Relaxation of TLS and GOT relocations where appropriate. - Platform-specific test cases. In addition to new platform code and the obvious changes, there were a few additional changes to common code: - Add three new RelExpr members (R_GOTPLT_OFF, R_GOTPLT_PC, and R_PLT_GOTREL) needed to support certain s390x relocations. I chose not to use a platform-specific name since nothing in the definition of these relocs is actually platform-specific; it is well possible that other platforms will need the same. - A couple of tweaks to TLS relocation handling, as the particular semantics of the s390x versions differ slightly. See comments in the code. This was tested by building and testing >1500 Fedora packages, with only a handful of failures; as these also have issues when building with LLD on other architectures, they seem unrelated. Co-authored-by: Tulio Magno Quites Machado Filho --- lld/ELF/Arch/SystemZ.cpp | 587 ++++++++++++++++++++ lld/ELF/CMakeLists.txt | 1 + lld/ELF/Driver.cpp | 3 +- lld/ELF/InputFiles.cpp | 2 + lld/ELF/InputSection.cpp | 7 + lld/ELF/Relocations.cpp | 24 +- lld/ELF/Relocations.h | 3 + lld/ELF/ScriptParser.cpp | 1 + lld/ELF/SyntheticSections.cpp | 3 + lld/ELF/Target.cpp | 2 + lld/ELF/Target.h | 1 + lld/test/ELF/Inputs/systemz-init.s | 5 + lld/test/ELF/basic-systemz.s | 63 +++ lld/test/ELF/emulation-systemz.s | 29 + lld/test/ELF/lto/systemz.ll | 18 + lld/test/ELF/systemz-gnu-ifunc.s | 72 +++ lld/test/ELF/systemz-got.s | 16 + lld/test/ELF/systemz-gotent-relax-align.s | 48 ++ lld/test/ELF/systemz-gotent-relax-und-dso.s | 68 +++ lld/test/ELF/systemz-gotent-relax.s | 92 +++ lld/test/ELF/systemz-init-padding.s | 27 + lld/test/ELF/systemz-pie.s | 38 ++ lld/test/ELF/systemz-plt.s | 83 +++ lld/test/ELF/systemz-reloc-abs.s | 29 + lld/test/ELF/systemz-reloc-disp12.s | 21 + lld/test/ELF/systemz-reloc-disp20.s | 21 + lld/test/ELF/systemz-reloc-pc16.s | 39 ++ lld/test/ELF/systemz-reloc-pc32.s | 39 ++ lld/test/ELF/systemz-reloc-range12.s | 16 + lld/test/ELF/systemz-reloc-range16.s | 16 + lld/test/ELF/systemz-reloc-range24.s | 16 + lld/test/ELF/systemz-reloc-range32.s | 16 + lld/test/ELF/systemz-shared.s | 86 +++ lld/test/ELF/systemz-tls-gd.s | 142 +++++ lld/test/ELF/systemz-tls-ie.s | 74 +++ lld/test/ELF/systemz-tls-ld.s | 114 ++++ lld/test/ELF/systemz-tls-le.s | 61 ++ lld/test/lit.cfg.py | 1 + 38 files changed, 1881 insertions(+), 3 deletions(-) create mode 100644 lld/ELF/Arch/SystemZ.cpp create mode 100644 lld/test/ELF/Inputs/systemz-init.s create mode 100644 lld/test/ELF/basic-systemz.s create mode 100644 lld/test/ELF/emulation-systemz.s create mode 100644 lld/test/ELF/lto/systemz.ll create mode 100644 lld/test/ELF/systemz-gnu-ifunc.s create mode 100644 lld/test/ELF/systemz-got.s create mode 100644 lld/test/ELF/systemz-gotent-relax-align.s create mode 100644 lld/test/ELF/systemz-gotent-relax-und-dso.s create mode 100644 lld/test/ELF/systemz-gotent-relax.s create mode 100644 lld/test/ELF/systemz-init-padding.s create mode 100644 lld/test/ELF/systemz-pie.s create mode 100644 lld/test/ELF/systemz-plt.s create mode 100644 lld/test/ELF/systemz-reloc-abs.s create mode 100644 lld/test/ELF/systemz-reloc-disp12.s create mode 100644 lld/test/ELF/systemz-reloc-disp20.s create mode 100644 lld/test/ELF/systemz-reloc-pc16.s create mode 100644 lld/test/ELF/systemz-reloc-pc32.s create mode 100644 lld/test/ELF/systemz-reloc-range12.s create mode 100644 lld/test/ELF/systemz-reloc-range16.s create mode 100644 lld/test/ELF/systemz-reloc-range24.s create mode 100644 lld/test/ELF/systemz-reloc-range32.s create mode 100644 lld/test/ELF/systemz-shared.s create mode 100644 lld/test/ELF/systemz-tls-gd.s create mode 100644 lld/test/ELF/systemz-tls-ie.s create mode 100644 lld/test/ELF/systemz-tls-ld.s create mode 100644 lld/test/ELF/systemz-tls-le.s diff --git a/lld/ELF/Arch/SystemZ.cpp b/lld/ELF/Arch/SystemZ.cpp new file mode 100644 index 00000000000000..8b55e063c3e53f --- /dev/null +++ b/lld/ELF/Arch/SystemZ.cpp @@ -0,0 +1,587 @@ +//===- SystemZ.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "OutputSections.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class SystemZ : public TargetInfo { +public: + SystemZ(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void addPltHeaderSymbols(InputSection &isd) const override; + void writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; + RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; + RelExpr adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const override; + bool relaxOnce(int pass) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + +private: + void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; +}; +} // namespace + +SystemZ::SystemZ() { + copyRel = R_390_COPY; + gotRel = R_390_GLOB_DAT; + pltRel = R_390_JMP_SLOT; + relativeRel = R_390_RELATIVE; + iRelativeRel = R_390_IRELATIVE; + symbolicRel = R_390_64; + tlsGotRel = R_390_TLS_TPOFF; + tlsModuleIndexRel = R_390_TLS_DTPMOD; + tlsOffsetRel = R_390_TLS_DTPOFF; + gotHeaderEntriesNum = 3; + gotPltHeaderEntriesNum = 0; + gotEntrySize = 8; + pltHeaderSize = 32; + pltEntrySize = 32; + ipltEntrySize = 32; + + // This "trap instruction" is used to fill gaps between sections. + // On SystemZ, the behavior of the GNU ld is to fill those gaps + // with nop instructions instead - and unfortunately the default + // glibc crt object files (used to) rely on that behavior since + // they use an alignment on the .init section fragments that causes + // gaps which must be filled with nops as they are being executed. + // Therefore, we provide a nop instruction as "trapInstr" here. + trapInstr = {0x07, 0x07, 0x07, 0x07}; + + defaultImageBase = 0x1000000; +} + +RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_390_NONE: + return R_NONE; + // Relocations targeting the symbol value. + case R_390_8: + case R_390_12: + case R_390_16: + case R_390_20: + case R_390_32: + case R_390_64: + return R_ABS; + case R_390_PC16: + case R_390_PC32: + case R_390_PC64: + case R_390_PC12DBL: + case R_390_PC16DBL: + case R_390_PC24DBL: + case R_390_PC32DBL: + return R_PC; + case R_390_GOTOFF16: + case R_390_GOTOFF: // a.k.a. R_390_GOTOFF32 + case R_390_GOTOFF64: + return R_GOTREL; + // Relocations targeting the PLT associated with the symbol. + case R_390_PLT32: + case R_390_PLT64: + case R_390_PLT12DBL: + case R_390_PLT16DBL: + case R_390_PLT24DBL: + case R_390_PLT32DBL: + return R_PLT_PC; + case R_390_PLTOFF16: + case R_390_PLTOFF32: + case R_390_PLTOFF64: + return R_PLT_GOTREL; + // Relocations targeting the GOT entry associated with the symbol. + case R_390_GOTENT: + return R_GOT_PC; + case R_390_GOT12: + case R_390_GOT16: + case R_390_GOT20: + case R_390_GOT32: + case R_390_GOT64: + return R_GOT_OFF; + // Relocations targeting the GOTPLT entry associated with the symbol. + case R_390_GOTPLTENT: + return R_GOTPLT_PC; + case R_390_GOTPLT12: + case R_390_GOTPLT16: + case R_390_GOTPLT20: + case R_390_GOTPLT32: + case R_390_GOTPLT64: + return R_GOTPLT_OFF; + // Relocations targeting _GLOBAL_OFFSET_TABLE_. + case R_390_GOTPC: + case R_390_GOTPCDBL: + return R_GOTONLY_PC; + // TLS-related relocations. + case R_390_TLS_LOAD: + return R_NONE; + case R_390_TLS_GDCALL: + return R_TLSGD_PC; + case R_390_TLS_LDCALL: + return R_TLSLD_PC; + case R_390_TLS_GD32: + case R_390_TLS_GD64: + return R_TLSGD_GOT; + case R_390_TLS_LDM32: + case R_390_TLS_LDM64: + return R_TLSLD_GOT; + case R_390_TLS_LDO32: + case R_390_TLS_LDO64: + return R_DTPREL; + case R_390_TLS_LE32: + case R_390_TLS_LE64: + return R_TPREL; + case R_390_TLS_IE32: + case R_390_TLS_IE64: + return R_GOT; + case R_390_TLS_GOTIE12: + case R_390_TLS_GOTIE20: + case R_390_TLS_GOTIE32: + case R_390_TLS_GOTIE64: + return R_GOT_OFF; + case R_390_TLS_IEENT: + return R_GOT_PC; + + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +void SystemZ::writeGotHeader(uint8_t *buf) const { + // _GLOBAL_OFFSET_TABLE_[0] holds the value of _DYNAMIC. + // _GLOBAL_OFFSET_TABLE_[1] and [2] are reserved. + write64be(buf, mainPart->dynamic->getVA()); +} + +void SystemZ::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write64be(buf, s.getPltVA() + 14); +} + +void SystemZ::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + if (config->writeAddends) + write64be(buf, s.getVA()); +} + +void SystemZ::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { + 0xe3, 0x10, 0xf0, 0x38, 0x00, 0x24, // stg %r1,56(%r15) + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1,_GLOBAL_OFFSET_TABLE_ + 0xd2, 0x07, 0xf0, 0x30, 0x10, 0x08, // mvc 48(8,%r15),8(%r1) + 0xe3, 0x10, 0x10, 0x10, 0x00, 0x04, // lg %r1,16(%r1) + 0x07, 0xf1, // br %r1 + 0x07, 0x00, // nopr + 0x07, 0x00, // nopr + 0x07, 0x00, // nopr + }; + memcpy(buf, pltData, sizeof(pltData)); + uint64_t got = in.got->getVA(); + uint64_t plt = in.plt->getVA(); + write32be(buf + 8, (got - plt - 6) >> 1); +} + +void SystemZ::addPltHeaderSymbols(InputSection &isec) const { + // The PLT header needs a reference to _GLOBAL_OFFSET_TABLE_, so we + // must ensure the .got section is created even if otherwise unused. + in.got->hasGotOffRel.store(true, std::memory_order_relaxed); +} + +void SystemZ::writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const { + const uint8_t inst[] = { + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1,<.got.plt slot> + 0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, // lg %r1,0(%r1) + 0x07, 0xf1, // br %r1 + 0x0d, 0x10, // basr %r1,%r0 + 0xe3, 0x10, 0x10, 0x0c, 0x00, 0x14, // lgf %r1,12(%r1) + 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, // jg + 0x00, 0x00, 0x00, 0x00, // + }; + memcpy(buf, inst, sizeof(inst)); + + write32be(buf + 2, (sym.getGotPltVA() - pltEntryAddr) >> 1); + write32be(buf + 24, (in.plt->getVA() - pltEntryAddr - 22) >> 1); + write32be(buf + 28, in.relaPlt->entsize * sym.getPltIdx()); +} + +int64_t SystemZ::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_390_8: + return SignExtend64<8>(*buf); + case R_390_16: + case R_390_PC16: + return SignExtend64<16>(read16be(buf)); + case R_390_PC16DBL: + return SignExtend64<16>(read16be(buf)) << 1; + case R_390_32: + case R_390_PC32: + return SignExtend64<32>(read32be(buf)); + case R_390_PC32DBL: + return SignExtend64<32>(read32be(buf)) << 1; + case R_390_64: + case R_390_PC64: + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + case R_390_GLOB_DAT: + case R_390_RELATIVE: + case R_390_IRELATIVE: + return read64be(buf); + case R_390_COPY: + case R_390_JMP_SLOT: + case R_390_NONE: + // These relocations are defined as not having an implicit addend. + return 0; + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + +RelType SystemZ::getDynRel(RelType type) const { + if (type == R_390_64 || type == R_390_PC64) + return type; + return R_390_NONE; +} + +RelExpr SystemZ::adjustTlsExpr(RelType type, RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + return expr; +} + +void SystemZ::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + // The general-dynamic code sequence for a global `x`: + // + // Instruction Relocation Symbol + // ear %rX,%a0 + // sllg %rX,%rX,32 + // ear %rX,%a1 + // larl %r12,_GLOBAL_OFFSET_TABLE_ R_390_GOTPCDBL _GLOBAL_OFFSET_TABLE_ + // lgrl %r2,.LC0 R_390_PC32DBL .LC0 + // brasl %r14,__tls_get_offset@plt R_390_TLS_GDCALL x + // :tls_gdcall:x R_390_PLT32DBL __tls_get_offset + // la %r2,0(%r2,%rX) + // + // .LC0: + // .quad x@TLSGD R_390_TLS_GD64 x + // + // Relaxing to initial-exec entails: + // 1) Replacing the call by a load from the GOT. + // 2) Replacing the relocation on the constant LC0 by R_390_TLS_GOTIE64. + + switch (rel.type) { + case R_390_TLS_GDCALL: + /* brasl %r14,__tls_get_offset@plt -> lg %r2,0(%r2,%r12) */ + write16be(loc, 0xe322); + write16be(loc + 2, 0xc000); + write16be(loc + 4, 0x0004); + break; + case R_390_TLS_GD64: + relocateNoSym(loc, R_390_TLS_GOTIE64, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void SystemZ::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + // The general-dynamic code sequence for a global `x`: + // + // Instruction Relocation Symbol + // ear %rX,%a0 + // sllg %rX,%rX,32 + // ear %rX,%a1 + // larl %r12,_GLOBAL_OFFSET_TABLE_ R_390_GOTPCDBL _GLOBAL_OFFSET_TABLE_ + // lgrl %r2,.LC0 R_390_PC32DBL .LC0 + // brasl %r14,__tls_get_offset@plt R_390_TLS_GDCALL x + // :tls_gdcall:x R_390_PLT32DBL __tls_get_offset + // la %r2,0(%r2,%rX) + // + // .LC0: + // .quad x@tlsgd R_390_TLS_GD64 x + // + // Relaxing to local-exec entails: + // 1) Replacing the call by a nop. + // 2) Replacing the relocation on the constant LC0 by R_390_TLS_LE64. + + switch (rel.type) { + case R_390_TLS_GDCALL: + /* brasl %r14,__tls_get_offset@plt -> brcl 0,. */ + write16be(loc, 0xc004); + write16be(loc + 2, 0x0000); + write16be(loc + 4, 0x0000); + break; + case R_390_TLS_GD64: + relocateNoSym(loc, R_390_TLS_LE64, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void SystemZ::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + // The local-dynamic code sequence for a global `x`: + // + // Instruction Relocation Symbol + // ear %rX,%a0 + // sllg %rX,%rX,32 + // ear %rX,%a1 + // larl %r12,_GLOBAL_OFFSET_TABLE_ R_390_GOTPCDBL _GLOBAL_OFFSET_TABLE_ + // lgrl %r2,.LC0 R_390_PC32DBL .LC0 + // brasl %r14,__tls_get_offset@plt R_390_TLS_LDCALL + // :tls_ldcall: R_390_PLT32DBL __tls_get_offset + // la %r2,0(%r2,%rX) + // lgrl %rY,.LC1 R_390_PC32DBL .LC1 + // la %r2,0(%r2,%rY) + // + // .LC0: + // .quad @tlsldm R_390_TLS_LDM64 + // .LC1: + // .quad x@dtpoff R_390_TLS_LDO64 x + // + // Relaxing to local-exec entails: + // 1) Replacing the call by a nop. + // 2) Replacing the constant LC0 by 0 (i.e. ignoring the relocation). + // 3) Replacing the relocation on the constant LC1 by R_390_TLS_LE64. + + switch (rel.type) { + case R_390_TLS_LDCALL: + /* brasl %r14,__tls_get_offset@plt -> brcl 0,. */ + write16be(loc, 0xc004); + write16be(loc + 2, 0x0000); + write16be(loc + 4, 0x0000); + break; + case R_390_TLS_LDM64: + break; + case R_390_TLS_LDO64: + relocateNoSym(loc, R_390_TLS_LE64, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +RelExpr SystemZ::adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const { + // Only R_390_GOTENT with addend 2 can be relaxed. + if (!config->relax || addend != 2 || type != R_390_GOTENT) + return R_GOT_PC; + const uint16_t op = read16be(loc - 2); + + // lgrl rx,sym@GOTENT -> larl rx, sym + // This relaxation is legal if "sym" binds locally (which was + // already verified by our caller) and is in-range and properly + // aligned for a LARL instruction. We cannot verify the latter + // constraint here, so we assume it is true and revert the decision + // later on in relaxOnce if necessary. + if ((op & 0xff0f) == 0xc408) + return R_RELAX_GOT_PC; + + return R_GOT_PC; +} + +bool SystemZ::relaxOnce(int pass) const { + // If we decided in adjustGotPcExpr to relax a R_390_GOTENT, + // we need to validate the target symbol is in-range and aligned. + SmallVector storage; + bool changed = false; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + for (Relocation &rel : sec->relocs()) { + if (rel.expr != R_RELAX_GOT_PC) + continue; + + uint64_t v = sec->getRelocTargetVA( + sec->file, rel.type, rel.addend, + sec->getOutputSection()->addr + rel.offset, *rel.sym, rel.expr); + if (isInt<33>(v) && !(v & 1)) + continue; + if (rel.sym->auxIdx == 0) { + rel.sym->allocateAux(); + addGotEntry(*rel.sym); + changed = true; + } + rel.expr = R_GOT_PC; + } + } + } + return changed; +} + +void SystemZ::relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + assert(isInt<33>(val) && + "R_390_GOTENT should not have been relaxed if it overflows"); + assert(!(val & 1) && + "R_390_GOTENT should not have been relaxed if it is misaligned"); + const uint16_t op = read16be(loc - 2); + + // lgrl rx,sym@GOTENT -> larl rx, sym + if ((op & 0xff0f) == 0xc408) { + write16be(loc - 2, 0xc000 | (op & 0x00f0)); + write32be(loc, val >> 1); + return; + } +} + +void SystemZ::relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.expr) { + case R_RELAX_GOT_PC: + return relaxGot(loc, rel, val); + case R_RELAX_TLS_GD_TO_IE_GOT_OFF: + return relaxTlsGdToIe(loc, rel, val); + case R_RELAX_TLS_GD_TO_LE: + return relaxTlsGdToLe(loc, rel, val); + case R_RELAX_TLS_LD_TO_LE: + return relaxTlsLdToLe(loc, rel, val); + default: + break; + } + switch (rel.type) { + case R_390_8: + checkIntUInt(loc, val, 8, rel); + *loc = val; + break; + case R_390_12: + case R_390_GOT12: + case R_390_GOTPLT12: + case R_390_TLS_GOTIE12: + checkUInt(loc, val, 12, rel); + write16be(loc, (read16be(loc) & 0xF000) | val); + break; + case R_390_PC12DBL: + case R_390_PLT12DBL: + checkInt(loc, val, 13, rel); + checkAlignment(loc, val, 2, rel); + write16be(loc, (read16be(loc) & 0xF000) | (val >> 1)); + break; + case R_390_16: + case R_390_GOT16: + case R_390_GOTPLT16: + case R_390_GOTOFF16: + case R_390_PLTOFF16: + checkIntUInt(loc, val, 16, rel); + write16be(loc, val); + break; + case R_390_PC16: + checkInt(loc, val, 16, rel); + write16be(loc, val); + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + checkInt(loc, val, 17, rel); + checkAlignment(loc, val, 2, rel); + write16be(loc, val >> 1); + break; + case R_390_20: + case R_390_GOT20: + case R_390_GOTPLT20: + case R_390_TLS_GOTIE20: + checkInt(loc, val, 20, rel); + write32be(loc, (read32be(loc) & 0xF00000FF) | ((val & 0xFFF) << 16) | + ((val & 0xFF000) >> 4)); + break; + case R_390_PC24DBL: + case R_390_PLT24DBL: + checkInt(loc, val, 25, rel); + loc[0] = val >> 17; + loc[1] = val >> 9; + loc[2] = val >> 1; + break; + case R_390_32: + case R_390_GOT32: + case R_390_GOTPLT32: + case R_390_GOTOFF: + case R_390_PLTOFF32: + case R_390_TLS_IE32: + case R_390_TLS_GOTIE32: + case R_390_TLS_GD32: + case R_390_TLS_LDM32: + case R_390_TLS_LDO32: + case R_390_TLS_LE32: + checkIntUInt(loc, val, 32, rel); + write32be(loc, val); + break; + case R_390_PC32: + case R_390_PLT32: + checkInt(loc, val, 32, rel); + write32be(loc, val); + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + case R_390_GOTPCDBL: + case R_390_GOTENT: + case R_390_GOTPLTENT: + case R_390_TLS_IEENT: + checkInt(loc, val, 33, rel); + checkAlignment(loc, val, 2, rel); + write32be(loc, val >> 1); + break; + case R_390_64: + case R_390_PC64: + case R_390_PLT64: + case R_390_GOT64: + case R_390_GOTPLT64: + case R_390_GOTOFF64: + case R_390_PLTOFF64: + case R_390_GOTPC: + case R_390_TLS_IE64: + case R_390_TLS_GOTIE64: + case R_390_TLS_GD64: + case R_390_TLS_LDM64: + case R_390_TLS_LDO64: + case R_390_TLS_LE64: + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + write64be(loc, val); + break; + case R_390_TLS_LOAD: + case R_390_TLS_GDCALL: + case R_390_TLS_LDCALL: + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +TargetInfo *elf::getSystemZTargetInfo() { + static SystemZ t; + return &t; +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 475f7dea1dd7e9..83d816ddb0601e 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -33,6 +33,7 @@ add_lld_library(lldELF Arch/PPC64.cpp Arch/RISCV.cpp Arch/SPARCV9.cpp + Arch/SystemZ.cpp Arch/X86.cpp Arch/X86_64.cpp ARMErrataFix.cpp diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 62e1c29504ba26..b2ea3840dce61a 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -200,6 +200,7 @@ static std::tuple parseEmulation(StringRef emul) { .Case("msp430elf", {ELF32LEKind, EM_MSP430}) .Case("elf64_amdgpu", {ELF64LEKind, EM_AMDGPU}) .Case("elf64loongarch", {ELF64LEKind, EM_LOONGARCH}) + .Case("elf64_s390", {ELF64BEKind, EM_S390}) .Default({ELFNoneKind, EM_NONE}); if (ret.first == ELFNoneKind) @@ -1136,7 +1137,7 @@ static SmallVector getSymbolOrderingFile(MemoryBufferRef mb) { static bool getIsRela(opt::InputArgList &args) { // The psABI specifies the default relocation entry format. bool rela = is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, - EM_PPC, EM_PPC64, EM_RISCV, EM_X86_64}, + EM_PPC, EM_PPC64, EM_RISCV, EM_S390, EM_X86_64}, config->emachine); // If -z rel or -z rela is specified, use the last option. for (auto *arg : args.filtered(OPT_z)) { diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 75e5ee1d0da4f5..3a443ff105205c 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1614,6 +1614,8 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { return EM_RISCV; case Triple::sparcv9: return EM_SPARCV9; + case Triple::systemz: + return EM_S390; case Triple::x86: return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index c728dd6c6306aa..54b1af597897c9 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -654,6 +654,7 @@ static int64_t getTlsTpOffset(const Symbol &s) { // Variant 2. case EM_HEXAGON: + case EM_S390: case EM_SPARCV9: case EM_386: case EM_X86_64: @@ -708,6 +709,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, case R_GOT_OFF: case R_RELAX_TLS_GD_TO_IE_GOT_OFF: return sym.getGotOffset() + a; + case R_GOTPLT_OFF: + return sym.getGotPltOffset() + a; case R_AARCH64_GOT_PAGE_PC: case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: return getAArch64Page(sym.getGotVA() + a) - getAArch64Page(p); @@ -716,6 +719,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, case R_GOT_PC: case R_RELAX_TLS_GD_TO_IE: return sym.getGotVA() + a - p; + case R_GOTPLT_PC: + return sym.getGotPltVA() + a - p; case R_LOONGARCH_GOT_PAGE_PC: if (sym.hasFlag(NEEDS_TLSGD)) return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p, type); @@ -807,6 +812,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, return getLoongArchPageDelta(sym.getPltVA() + a, p, type); case R_PLT_GOTPLT: return sym.getPltVA() + a - in.gotPlt->getVA(); + case R_PLT_GOTREL: + return sym.getPltVA() + a - in.got->getVA(); case R_PPC32_PLTREL: // R_PPC_PLTREL24 uses the addend (usually 0 or 0x8000) to indicate r30 // stores _GLOBAL_OFFSET_TABLE_ or .got2+0x8000. The addend is ignored for diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index b6a317bc3b6d69..c51be5444bbad0 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1364,8 +1364,8 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, R_LOONGARCH_GOT_PAGE_PC, R_GOT_OFF, R_TLSIE_HINT>(expr)) { ctx.hasTlsIe.store(true, std::memory_order_relaxed); // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally - // defined. - if (toExecRelax && isLocalInExecutable) { + // defined. This is not supported on SystemZ. + if (toExecRelax && isLocalInExecutable && config->emachine != EM_S390) { c.addReloc({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym}); } else if (expr != R_TLSIE_HINT) { sym.setFlags(NEEDS_TLSIE); @@ -1411,6 +1411,26 @@ template void RelocationScanner::scanOne(RelTy *&i) { if (expr == R_NONE) return; + // Like other platforms, calls to the TLS helper routine on SystemZ carry + // two relocations, one for the helper routine itself, and a TLS marker + // relocation. When relaxing the TLS model, the helper routine is no longer + // needed, and its relocation should be removed. Unlike other platforms, + // on SystemZ the TLS marker routine typically comes *after* the helper + // routine relocation, so the getTlsGdRelaxSkip mechanism used by + // handleTlsRelocation does not work on this platform. + // + // Instead, check for this case here: if we are building a main executable + // (i.e. TLS relaxation applies), and the relocation *after* the current one + // is a TLS call marker instruction matching the current instruction, then + // skip this relocation. + if (config->emachine == EM_S390 && !config->shared) { + if (i < end && getter.get(i->r_offset) == offset - 2) { + RelType nextType = i->getType(/*isMips64EL=*/false); + if (nextType == R_390_TLS_GDCALL || nextType == R_390_TLS_LDCALL) + return; + } + } + // Error if the target symbol is undefined. Symbol index 0 may be used by // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them. if (sym.isUndefined() && symIndex != 0 && diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index cfb9092149f3e0..acfdf56e837694 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -40,11 +40,14 @@ enum RelExpr { R_GOTPLT, R_GOTPLTREL, R_GOTREL, + R_GOTPLT_OFF, + R_GOTPLT_PC, R_NONE, R_PC, R_PLT, R_PLT_PC, R_PLT_GOTPLT, + R_PLT_GOTREL, R_RELAX_HINT, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index dd69916d6b05e8..f0ede1f43bbdb3 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -445,6 +445,7 @@ static std::pair parseBfdName(StringRef s) { .Case("elf32-msp430", {ELF32LEKind, EM_MSP430}) .Case("elf32-loongarch", {ELF32LEKind, EM_LOONGARCH}) .Case("elf64-loongarch", {ELF64LEKind, EM_LOONGARCH}) + .Case("elf64-s390", {ELF64BEKind, EM_S390}) .Default({ELFNoneKind, EM_NONE}); } diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 4b413163314b2e..bada394aa30d7d 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -1419,6 +1419,9 @@ DynamicSection::computeContents() { case EM_MIPS: addInSec(DT_MIPS_PLTGOT, *in.gotPlt); break; + case EM_S390: + addInSec(DT_PLTGOT, *in.got); + break; case EM_SPARCV9: addInSec(DT_PLTGOT, *in.plt); break; diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 671d22cc66a0e9..b7922425a34e43 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -87,6 +87,8 @@ TargetInfo *elf::getTarget() { return getRISCVTargetInfo(); case EM_SPARCV9: return getSPARCV9TargetInfo(); + case EM_S390: + return getSystemZTargetInfo(); case EM_X86_64: return getX86_64TargetInfo(); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index ab6b6b9c013ba3..3c06789cdbd361 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -186,6 +186,7 @@ TargetInfo *getPPC64TargetInfo(); TargetInfo *getPPCTargetInfo(); TargetInfo *getRISCVTargetInfo(); TargetInfo *getSPARCV9TargetInfo(); +TargetInfo *getSystemZTargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); template TargetInfo *getMipsTargetInfo(); diff --git a/lld/test/ELF/Inputs/systemz-init.s b/lld/test/ELF/Inputs/systemz-init.s new file mode 100644 index 00000000000000..1611b69b4419e3 --- /dev/null +++ b/lld/test/ELF/Inputs/systemz-init.s @@ -0,0 +1,5 @@ +// glibc < 2.39 used to align .init and .fini code at a 4-byte boundary. +// This file aims to recreate that behavior. + .section .init,"ax",@progbits + .align 4 + lg %r4, 272(%r15) diff --git a/lld/test/ELF/basic-systemz.s b/lld/test/ELF/basic-systemz.s new file mode 100644 index 00000000000000..e65b73c91f6e14 --- /dev/null +++ b/lld/test/ELF/basic-systemz.s @@ -0,0 +1,63 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o +# RUN: ld.lld --hash-style=sysv -discard-all -shared %t.o -o %t.so +# RUN: llvm-readelf --file-header --program-headers --section-headers --dynamic-table %t.so | FileCheck %s + +# Exits with return code 55 on linux. +.text + lghi 2,55 + svc 1 + +// CHECK: ELF Header: +// CHECK-NEXT: Magic: 7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00 +// CHECK-NEXT: Class: ELF64 +// CHECK-NEXT: Data: 2's complement, big endian +// CHECK-NEXT: Version: 1 (current) +// CHECK-NEXT: OS/ABI: UNIX - System V +// CHECK-NEXT: ABI Version: 0 +// CHECK-NEXT: Type: DYN (Shared object file) +// CHECK-NEXT: Machine: IBM S/390 +// CHECK-NEXT: Version: 0x1 +// CHECK-NEXT: Entry point address: 0x0 +// CHECK-NEXT: Start of program headers: 64 (bytes into file) +// CHECK-NEXT: Start of section headers: 768 (bytes into file) +// CHECK-NEXT: Flags: 0x0 +// CHECK-NEXT: Size of this header: 64 (bytes) +// CHECK-NEXT: Size of program headers: 56 (bytes) +// CHECK-NEXT: Number of program headers: 7 +// CHECK-NEXT: Size of section headers: 64 (bytes) +// CHECK-NEXT: Number of section headers: 11 +// CHECK-NEXT: Section header string table index: 9 + +// CHECK: Section Headers: +// CHECK-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +// CHECK-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +// CHECK-NEXT: [ 1] .dynsym DYNSYM 00000000000001c8 0001c8 000018 18 A 3 1 8 +// CHECK-NEXT: [ 2] .hash HASH 00000000000001e0 0001e0 000010 04 A 1 0 4 +// CHECK-NEXT: [ 3] .dynstr STRTAB 00000000000001f0 0001f0 000001 00 A 0 0 1 +// CHECK-NEXT: [ 4] .text PROGBITS 00000000000011f4 0001f4 000006 00 AX 0 0 4 +// CHECK-NEXT: [ 5] .dynamic DYNAMIC 0000000000002200 000200 000060 10 WA 3 0 8 +// CHECK-NEXT: [ 6] .relro_padding NOBITS 0000000000002260 000260 000da0 00 WA 0 0 1 +// CHECK-NEXT: [ 7] .comment PROGBITS 0000000000000000 000260 000008 01 MS 0 0 1 +// CHECK-NEXT: [ 8] .symtab SYMTAB 0000000000000000 000268 000030 18 10 2 8 +// CHECK-NEXT: [ 9] .shstrtab STRTAB 0000000000000000 000298 000058 00 0 0 1 +// CHECK-NEXT: [10] .strtab STRTAB 0000000000000000 0002f0 00000a 00 0 0 1 + +// CHECK: Program Headers: +// CHECK-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +// CHECK-NEXT: PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000188 0x000188 R 0x8 +// CHECK-NEXT: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0001f1 0x0001f1 R 0x1000 +// CHECK-NEXT: LOAD 0x0001f4 0x00000000000011f4 0x00000000000011f4 0x000006 0x000006 R E 0x1000 +// CHECK-NEXT: LOAD 0x000200 0x0000000000002200 0x0000000000002200 0x000060 0x000e00 RW 0x1000 +// CHECK-NEXT: DYNAMIC 0x000200 0x0000000000002200 0x0000000000002200 0x000060 0x000060 RW 0x8 +// CHECK-NEXT: GNU_RELRO 0x000200 0x0000000000002200 0x0000000000002200 0x000060 0x000e00 R 0x1 +// CHECK-NEXT: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x0 + +// CHECK: Dynamic section at offset 0x200 contains 6 entries: +// CHECK-NEXT: Tag Type Name/Value +// CHECK-NEXT: 0x0000000000000006 (SYMTAB) 0x1c8 +// CHECK-NEXT: 0x000000000000000b (SYMENT) 24 (bytes) +// CHECK-NEXT: 0x0000000000000005 (STRTAB) 0x1f0 +// CHECK-NEXT: 0x000000000000000a (STRSZ) 1 (bytes) +// CHECK-NEXT: 0x0000000000000004 (HASH) 0x1e0 +// CHECK-NEXT: 0x0000000000000000 (NULL) 0x0 diff --git a/lld/test/ELF/emulation-systemz.s b/lld/test/ELF/emulation-systemz.s new file mode 100644 index 00000000000000..12e2c231d41ee3 --- /dev/null +++ b/lld/test/ELF/emulation-systemz.s @@ -0,0 +1,29 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t +# RUN: ld.lld -m elf64_s390 %t -o %t2 +# RUN: llvm-readelf --file-header %t2 | FileCheck %s +# RUN: ld.lld %t -o %t3 +# RUN: llvm-readelf --file-header %t3 | FileCheck %s +# RUN: echo 'OUTPUT_FORMAT(elf64-s390)' > %t.script +# RUN: ld.lld %t.script %t -o %t4 +# RUN: llvm-readelf --file-header %t4 | FileCheck %s + +// CHECK: ELF Header: +// CHECK-NEXT: Magic: 7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00 +// CHECK-NEXT: Class: ELF64 +// CHECK-NEXT: Data: 2's complement, big endian +// CHECK-NEXT: Version: 1 (current) +// CHECK-NEXT: OS/ABI: UNIX - System V +// CHECK-NEXT: ABI Version: 0 +// CHECK-NEXT: Type: EXEC (Executable file) +// CHECK-NEXT: Machine: IBM S/390 +// CHECK-NEXT: Version: 0x1 +// CHECK-NEXT: Entry point address: +// CHECK-NEXT: Start of program headers: 64 (bytes into file) +// CHECK-NEXT: Start of section headers: +// CHECK-NEXT: Flags: 0x0 +// CHECK-NEXT: Size of this header: 64 (bytes) +// CHECK-NEXT: Size of program headers: 56 (bytes) + +.globl _start +_start: diff --git a/lld/test/ELF/lto/systemz.ll b/lld/test/ELF/lto/systemz.ll new file mode 100644 index 00000000000000..42bf4e32fb6d75 --- /dev/null +++ b/lld/test/ELF/lto/systemz.ll @@ -0,0 +1,18 @@ +; REQUIRES: systemz +;; Test we can infer the e_machine value EM_S390 from a bitcode file. + +; RUN: llvm-as %s -o %t.o +; RUN: ld.lld %t.o -o %t +; RUN: llvm-readobj -h %t | FileCheck %s + +; CHECK: Class: 64-bit +; CHECK: DataEncoding: BigEndian +; CHECK: Machine: EM_S390 + +target datalayout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64" +target triple = "s390x-unknown-linux-gnu" + +define void @_start() { +entry: + ret void +} diff --git a/lld/test/ELF/systemz-gnu-ifunc.s b/lld/test/ELF/systemz-gnu-ifunc.s new file mode 100644 index 00000000000000..a9f7d995a90894 --- /dev/null +++ b/lld/test/ELF/systemz-gnu-ifunc.s @@ -0,0 +1,72 @@ +// REQUIRES: systemz +// RUN: llvm-mc -filetype=obj -triple=s390x-none-linux-gnu %s -o %t.o +// RUN: ld.lld -static %t.o -o %tout +// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readelf --section-headers --relocations --symbols %tout | FileCheck %s + +// CHECK: There are 9 section headers +// CHECK: Section Headers: +// CHECK-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +// CHECK-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +// CHECK-NEXT: [ 1] .rela.dyn RELA 0000000001000158 000158 000030 18 AI 0 4 8 +// CHECK-NEXT: [ 2] .text PROGBITS 0000000001001188 000188 00001c 00 AX 0 0 4 +// CHECK-NEXT: [ 3] .iplt PROGBITS 00000000010011b0 0001b0 000040 00 AX 0 0 16 +// CHECK-NEXT: [ 4] .got.plt PROGBITS 00000000010021f0 0001f0 000010 00 WA 0 0 8 +// CHECK-NEXT: [ 5] .comment PROGBITS 0000000000000000 000200 000008 01 MS 0 0 1 +// CHECK-NEXT: [ 6] .symtab SYMTAB 0000000000000000 000208 000090 18 8 3 8 +// CHECK-NEXT: [ 7] .shstrtab STRTAB 0000000000000000 000298 000043 00 0 0 1 +// CHECK-NEXT: [ 8] .strtab STRTAB 0000000000000000 0002db 000032 00 0 0 1 + +// CHECK: Relocation section '.rela.dyn' at offset 0x158 contains 2 entries: +// CHECK-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend +// CHECK-NEXT: 00000000010021f0 000000000000003d R_390_IRELATIVE 1001188 +// CHECK-NEXT: 00000000010021f8 000000000000003d R_390_IRELATIVE 100118a + +// CHECK: Symbol table '.symtab' contains 6 entries: +// CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK-NEXT: 1: 0000000001000158 0 NOTYPE LOCAL HIDDEN 1 __rela_iplt_start +// CHECK-NEXT: 2: 0000000001000188 0 NOTYPE LOCAL HIDDEN 1 __rela_iplt_end +// CHECK-NEXT: 3: 0000000001001188 0 IFUNC GLOBAL DEFAULT 2 foo +// CHECK-NEXT: 4: 000000000100118a 0 IFUNC GLOBAL DEFAULT 2 bar +// CHECK-NEXT: 5: 000000000100118c 0 NOTYPE GLOBAL DEFAULT 2 _start + +// DISASM: Disassembly of section .text: +// DISASM-EMPTY: +// DISASM-NEXT: : +// DISASM-NEXT: 1001188: br %r14 +// DISASM: : +// DISASM-NEXT: 100118a: br %r14 +// DISASM: <_start>: +// DISASM-NEXT: 100118c: brasl %r14, 0x10011b0 +// DISASM-NEXT: 1001192: brasl %r14, 0x10011d0 +// DISASM-NEXT: 1001198: larl %r2, 0x1000158 +// DISASM-NEXT: 100119e: larl %r2, 0x1000188 +// DISASM-EMPTY: +// DISASM-NEXT: Disassembly of section .iplt: +// DISASM-EMPTY: +// DISASM-NEXT: <.iplt>: +// DISASM: 10011b0: larl %r1, 0x10021f0 +// DISASM-NEXT: 10011b6: lg %r1, 0(%r1) +// DISASM-NEXT: 10011bc: br %r1 +// DISASM: 10011d0: larl %r1, 0x10021f8 +// DISASM-NEXT: 10011d6: lg %r1, 0(%r1) +// DISASM-NEXT: 10011dc: br %r1 + +.text +.type foo STT_GNU_IFUNC +.globl foo +foo: + br %r14 + +.type bar STT_GNU_IFUNC +.globl bar +bar: + br %r14 + +.globl _start +_start: + brasl %r14, foo@plt + brasl %r14, bar@plt + larl %r2, __rela_iplt_start + larl %r2, __rela_iplt_end diff --git a/lld/test/ELF/systemz-got.s b/lld/test/ELF/systemz-got.s new file mode 100644 index 00000000000000..f89babad023669 --- /dev/null +++ b/lld/test/ELF/systemz-got.s @@ -0,0 +1,16 @@ +// REQUIRES: systemz +// RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %p/Inputs/shared.s -o %t2.o +// RUN: ld.lld -shared %t2.o -soname=%t2.so -o %t2.so + +// RUN: ld.lld -dynamic-linker /lib/ld64.so.1 %t.o %t2.so -o %t +// RUN: llvm-readelf -S -r %t | FileCheck %s + +// CHECK: .got PROGBITS {{.*}} {{.*}} 000020 00 WA 0 0 8 + +// CHECK: Relocation section '.rela.dyn' at offset {{.*}} contains 1 entries: +// CHECK: {{.*}} 000000010000000a R_390_GLOB_DAT 0000000000000000 bar + 0 + +.global _start +_start: + lgrl %r1,bar@GOT diff --git a/lld/test/ELF/systemz-gotent-relax-align.s b/lld/test/ELF/systemz-gotent-relax-align.s new file mode 100644 index 00000000000000..c6326086f56db0 --- /dev/null +++ b/lld/test/ELF/systemz-gotent-relax-align.s @@ -0,0 +1,48 @@ +# REQUIRES: systemz +## Verify that R_390_GOTENT optimization is not performed on misaligned symbols. + +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=s390x-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t1 +# RUN: llvm-readelf -S -r -x .got -x .got.plt %t1 | FileCheck --check-prefixes=CHECK %s +# RUN: llvm-objdump --no-print-imm-hex -d %t1 | FileCheck --check-prefix=DISASM %s + +## We retain one .got entry for the unaligned symbol. +# CHECK: Name Type Address Off Size ES Flg Lk Inf Al +# CHECK: .got PROGBITS 00000000010021e0 0001e0 000020 00 WA 0 0 8 +# CHECK-NEXT: .relro_padding NOBITS 0000000001002200 000200 000e00 00 WA 0 0 1 +# CHECK-NEXT: .data PROGBITS 0000000001003200 000200 000006 00 WA 0 0 2 + +# CHECK-LABEL: Hex dump of section '.got': +# CHECK-NEXT: 0x010021e0 00000000 00000000 00000000 00000000 +# CHECK-NEXT: 0x010021f0 00000000 00000000 00000000 01003205 + +# DISASM: Disassembly of section .text: +# DISASM: <_start>: +# DISASM-NEXT: larl %r1, 0x1003200 +# DISASM-NEXT: larl %r1, 0x1003200 +# DISASM-NEXT: lgrl %r1, 0x10021f8 +# DISASM-NEXT: lgrl %r1, 0x10021f8 + +.data +.globl var_align +.hidden var_align + .align 2 +var_align: + .long 0 + +.data +.globl var_unalign +.hidden var_unalign + .align 2 + .byte 0 +var_unalign: + .byte 0 + +.text +.globl _start +.type _start, @function +_start: + lgrl %r1, var_align@GOT + lgrl %r1, var_align@GOT + lgrl %r1, var_unalign@GOT + lgrl %r1, var_unalign@GOT diff --git a/lld/test/ELF/systemz-gotent-relax-und-dso.s b/lld/test/ELF/systemz-gotent-relax-und-dso.s new file mode 100644 index 00000000000000..57369a417fd445 --- /dev/null +++ b/lld/test/ELF/systemz-gotent-relax-und-dso.s @@ -0,0 +1,68 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=s390x-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=s390x-unknown-linux %S/Inputs/gotpc-relax-und-dso.s -o %tdso.o +# RUN: ld.lld -shared %tdso.o -soname=t.so -o %t.so +# RUN: ld.lld --hash-style=sysv -shared %t.o %t.so -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump --no-print-imm-hex -d %t | FileCheck --check-prefix=DISASM %s + +# RELOC-LABEL: Relocation section '.rela.dyn' at offset {{.*}} contains 3 entries: +# RELOC: 00000000000023f8 000000010000000a R_390_GLOB_DAT 00000000000012d8 foo + 0 +# RELOC: 0000000000002400 000000030000000a R_390_GLOB_DAT 0000000000000000 und + 0 +# RELOC: 0000000000002408 000000040000000a R_390_GLOB_DAT 0000000000000000 dsofoo + 0 + +# DISASM: Disassembly of section .text: +# DISASM-EMPTY: +# DISASM-NEXT: : +# DISASM-NEXT: bc 0, 0 +# DISASM: : +# DISASM-NEXT: bc 0, 0 +# DISASM: <_start>: +# DISASM-NEXT: lgrl %r1, 0x2400 +# DISASM-NEXT: lgrl %r1, 0x2400 +# DISASM-NEXT: lgrl %r1, 0x2408 +# DISASM-NEXT: lgrl %r1, 0x2408 +# DISASM-NEXT: larl %r1, 0x12dc +# DISASM-NEXT: larl %r1, 0x12dc +# DISASM-NEXT: lgrl %r1, 0x23f8 +# DISASM-NEXT: lgrl %r1, 0x23f8 +# DISASM-NEXT: lgrl %r1, 0x2400 +# DISASM-NEXT: lgrl %r1, 0x2400 +# DISASM-NEXT: lgrl %r1, 0x2408 +# DISASM-NEXT: lgrl %r1, 0x2408 +# DISASM-NEXT: larl %r1, 0x12dc +# DISASM-NEXT: larl %r1, 0x12dc +# DISASM-NEXT: lgrl %r1, 0x23f8 +# DISASM-NEXT: lgrl %r1, 0x23f8 + +.text +.globl foo +.type foo, @function +foo: + nop + +.globl hid +.hidden hid +.type hid, @function +hid: + nop + +.globl _start +.type _start, @function +_start: + lgrl %r1, und@GOT + lgrl %r1, und@GOT + lgrl %r1, dsofoo@GOT + lgrl %r1, dsofoo@GOT + lgrl %r1, hid@GOT + lgrl %r1, hid@GOT + lgrl %r1, foo@GOT + lgrl %r1, foo@GOT + lgrl %r1, und@GOT + lgrl %r1, und@GOT + lgrl %r1, dsofoo@GOT + lgrl %r1, dsofoo@GOT + lgrl %r1, hid@GOT + lgrl %r1, hid@GOT + lgrl %r1, foo@GOT + lgrl %r1, foo@GOT diff --git a/lld/test/ELF/systemz-gotent-relax.s b/lld/test/ELF/systemz-gotent-relax.s new file mode 100644 index 00000000000000..08e6a0e81b9f4c --- /dev/null +++ b/lld/test/ELF/systemz-gotent-relax.s @@ -0,0 +1,92 @@ +# REQUIRES: systemz +## Test R_390_GOTENT optimization. + +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=s390x-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t1 --no-apply-dynamic-relocs +# RUN: llvm-readelf -S -r -x .got.plt %t1 | FileCheck --check-prefixes=CHECK,NOAPPLY %s +# RUN: ld.lld %t.o -o %t1 --apply-dynamic-relocs +# RUN: llvm-readelf -S -r -x .got.plt %t1 | FileCheck --check-prefixes=CHECK,APPLY %s +# RUN: ld.lld %t.o -o %t1 +# RUN: llvm-objdump --no-print-imm-hex -d %t1 | FileCheck --check-prefix=DISASM %s + +## --no-relax disables GOT optimization. +# RUN: ld.lld --no-relax %t.o -o %t2 +# RUN: llvm-objdump --no-print-imm-hex -d %t2 | FileCheck --check-prefix=NORELAX %s + +## In our implementation, .got is retained even if all GOT-generating relocations are optimized. +# CHECK: Name Type Address Off Size ES Flg Lk Inf Al +# CHECK: .iplt PROGBITS 0000000001001240 000240 000020 00 AX 0 0 16 +# CHECK-NEXT: .got PROGBITS 0000000001002260 000260 000018 00 WA 0 0 8 +# CHECK-NEXT: .relro_padding NOBITS 0000000001002278 000278 000d88 00 WA 0 0 1 +# CHECK-NEXT: .got.plt PROGBITS 0000000001003278 000278 000008 00 WA 0 0 8 + +## There is one R_S390_IRELATIVE relocation. +# CHECK-LABEL: Relocation section '.rela.dyn' at offset {{.*}} contains 1 entries: +# CHECK: 0000000001003278 000000000000003d R_390_IRELATIVE 10011e8 + +# CHECK-LABEL: Hex dump of section '.got.plt': +# NOAPPLY-NEXT: 0x01003278 00000000 00000000 +# APPLY-NEXT: 0x01003278 00000000 010011e8 + +# DISASM: Disassembly of section .text: +# DISASM-EMPTY: +# DISASM-NEXT: : +# DISASM-NEXT: 10011e0: 47 00 00 00 bc 0, 0 +# DISASM: : +# DISASM-NEXT: 10011e4: 47 00 00 00 bc 0, 0 +# DISASM: : +# DISASM-NEXT: 10011e8: 07 fe br %r14 +# DISASM: <_start>: +# DISASM-NEXT: larl %r1, 0x10011e0 +# DISASM-NEXT: larl %r1, 0x10011e0 +# DISASM-NEXT: larl %r1, 0x10011e4 +# DISASM-NEXT: larl %r1, 0x10011e4 +# DISASM-NEXT: lgrl %r1, 0x1003278 +# DISASM-NEXT: lgrl %r1, 0x1003278 +# DISASM-NEXT: larl %r1, 0x10011e0 +# DISASM-NEXT: larl %r1, 0x10011e0 +# DISASM-NEXT: larl %r1, 0x10011e4 +# DISASM-NEXT: larl %r1, 0x10011e4 +# DISASM-NEXT: lgrl %r1, 0x1003278 +# DISASM-NEXT: lgrl %r1, 0x1003278 + +# NORELAX-LABEL: <_start>: +# NORELAX-COUNT-12: lgrl + +.text +.globl foo + +.text +.globl foo +.type foo, @function +foo: + nop + +.globl hid +.hidden hid +.type hid, @function +hid: + nop + +.text +.type ifunc STT_GNU_IFUNC +.globl ifunc +.type ifunc, @function +ifunc: + br %r14 + +.globl _start +.type _start, @function +_start: + lgrl %r1, foo@GOT + lgrl %r1, foo@GOT + lgrl %r1, hid@GOT + lgrl %r1, hid@GOT + lgrl %r1, ifunc@GOT + lgrl %r1, ifunc@GOT + lgrl %r1, foo@GOT + lgrl %r1, foo@GOT + lgrl %r1, hid@GOT + lgrl %r1, hid@GOT + lgrl %r1, ifunc@GOT + lgrl %r1, ifunc@GOT diff --git a/lld/test/ELF/systemz-init-padding.s b/lld/test/ELF/systemz-init-padding.s new file mode 100644 index 00000000000000..d522d4d7573e5c --- /dev/null +++ b/lld/test/ELF/systemz-init-padding.s @@ -0,0 +1,27 @@ +// REQUIRES: systemz +// RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %p/Inputs/systemz-init.s -o systemz-init.o +// RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o +// RUN: ld.lld -dynamic-linker /lib/ld64.so.1 %t.o systemz-init.o -o %t +// RUN: llvm-objdump -d --no-show-raw-insn -j .init %t | FileCheck %s + +// glibc < 2.39 used to align .init and .fini code at a 4-byte boundary. +// When that happens, the linker must not pad the code with invalid +// instructions, e.g. null bytes. + .section .init,"ax",@progbits + brasl %r14, startup + +// CHECK: <.init>: +// CHECK-NEXT: brasl %r14, +// CHECK-NEXT: bcr 0, %r7 +// CHECK-NEXT: lg %r4, 272(%r15) + + .text + .globl startup + .p2align 4 +startup: + br %r14 + + .globl main + .p2align 4 +main: + br %r14 diff --git a/lld/test/ELF/systemz-pie.s b/lld/test/ELF/systemz-pie.s new file mode 100644 index 00000000000000..bb971a82fb8ced --- /dev/null +++ b/lld/test/ELF/systemz-pie.s @@ -0,0 +1,38 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t1.o + +## Check -pie. +# RUN: ld.lld -pie %t1.o -o %t +# RUN: llvm-readelf --file-headers --program-headers --dynamic %t | FileCheck %s + +# CHECK: ELF Header: +# CHECK-NEXT: Magic: 7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00 +# CHECK-NEXT: Class: ELF64 +# CHECK-NEXT: Data: 2's complement, big endian +# CHECK-NEXT: Version: 1 (current) +# CHECK-NEXT: OS/ABI: UNIX - System V +# CHECK-NEXT: ABI Version: 0 +# CHECK-NEXT: Type: DYN (Shared object file) +# CHECK-NEXT: Machine: IBM S/390 +# CHECK-NEXT: Version: 0x1 + +# CHECK: Program Headers: +# CHECK-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# CHECK-NEXT: PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000188 0x000188 R 0x8 +# CHECK-NEXT: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x00020d 0x00020d R 0x1000 +# CHECK-NEXT: LOAD 0x000210 0x0000000000002210 0x0000000000002210 0x000090 0x000df0 RW 0x1000 +# CHECK-NEXT: DYNAMIC 0x000210 0x0000000000002210 0x0000000000002210 0x000090 0x000090 RW 0x8 +# CHECK-NEXT: GNU_RELRO 0x000210 0x0000000000002210 0x0000000000002210 0x000090 0x000df0 R 0x1 +# CHECK-NEXT: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x0 + +# CHECK: Dynamic section at offset 0x210 contains 9 entries: +# CHECK-NEXT: Tag Type Name/Value +# CHECK-NEXT: 0x000000006ffffffb (FLAGS_1) PIE + +## Check -nopie +# RUN: ld.lld -no-pie %t1.o -o %t2 +# RUN: llvm-readelf --file-headers %t2 | FileCheck %s --check-prefix=NOPIE +# NOPIE-NOT: Type: DYN + +.globl _start +_start: diff --git a/lld/test/ELF/systemz-plt.s b/lld/test/ELF/systemz-plt.s new file mode 100644 index 00000000000000..416a60a6866892 --- /dev/null +++ b/lld/test/ELF/systemz-plt.s @@ -0,0 +1,83 @@ +# REQUIRES: systemz +# RUN: echo '.globl bar, weak; .type bar,@function; .type weak,@function; bar: weak:' > %t1.s + +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %t1.s -o %t1.o +# RUN: ld.lld -shared %t1.o -soname=t1.so -o %t1.so +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o %t1.so -z separate-code -o %t +# RUN: llvm-readelf -S -s -r -x .got.plt %t | FileCheck %s +# RUN: llvm-objdump -d %t | FileCheck --check-prefixes=DIS %s + +# CHECK: Section Headers: +# CHECK: .plt PROGBITS 0000000001001020 001020 000060 00 AX 0 0 16 +# CHECK: .got PROGBITS 00000000010020d0 0020d0 000018 00 WA 0 0 8 +# CHECK: .got.plt PROGBITS 00000000010030e8 0020e8 000010 00 WA 0 0 8 + +# CHECK: Relocation section '.rela.plt' at offset {{.*}} contains 2 entries: +# CHECK: 00000000010030e8 000000010000000b R_390_JMP_SLOT 0000000000000000 bar + 0 +# CHECK: 00000000010030f0 000000020000000b R_390_JMP_SLOT 0000000000000000 weak + 0 + +## A canonical PLT has a non-zero st_value. bar and weak are called but their +## addresses are not taken, so a canonical PLT is not necessary. +# CHECK: Symbol table '.dynsym' contains 3 entries: +# CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +# CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# CHECK-NEXT: 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bar +# CHECK-NEXT: 2: 0000000000000000 0 FUNC WEAK DEFAULT UND weak + +## The .got.plt slots relocated by .rela.plt point to .plt +## This is required by glibc. +# CHECK: Hex dump of section '.got.plt': +# CHECK-NEXT: 0x010030e8 00000000 0100104e 00000000 0100106e + +# DIS: Disassembly of section .text: + +# DIS: 0000000001001000 <_start>: +# DIS-NEXT: 1001000: c0 e5 00 00 00 09 brasl %r14, 0x1001012 +# DIS-NEXT: 1001006: c0 e5 00 00 00 1d brasl %r14, 0x1001040 +# DIS-NEXT: 100100c: c0 e5 00 00 00 2a brasl %r14, 0x1001060 + +# DIS: 0000000001001012 : +# DIS-NEXT: 1001012: 07 fe br %r14 + +# DIS: Disassembly of section .plt: + +# DIS: 0000000001001020 <.plt>: +# DIS-NEXT: 1001020: e3 10 f0 38 00 24 stg %r1, 56(%r15) +# DIS-NEXT: 1001026: c0 10 00 00 08 55 larl %r1, 0x10020d0 +# DIS-NEXT: 100102c: d2 07 f0 30 10 08 mvc 48(8,%r15), 8(%r1) +# DIS-NEXT: 1001032: e3 10 10 10 00 04 lg %r1, 16(%r1) +# DIS-NEXT: 1001038: 07 f1 br %r1 +# DIS-NEXT: 100103a: 07 00 bcr 0, %r0 +# DIS-NEXT: 100103c: 07 00 bcr 0, %r0 +# DIS-NEXT: 100103e: 07 00 bcr 0, %r0 +# DIS-NEXT: 1001040: c0 10 00 00 10 54 larl %r1, 0x10030e8 +# DIS-NEXT: 1001046: e3 10 10 00 00 04 lg %r1, 0(%r1) +# DIS-NEXT: 100104c: 07 f1 br %r1 +# DIS-NEXT: 100104e: 0d 10 basr %r1, 0 +# DIS-NEXT: 1001050: e3 10 10 0c 00 14 lgf %r1, 12(%r1) +# DIS-NEXT: 1001056: c0 f4 ff ff ff e5 jg 0x1001020 +# DIS-NEXT: 100105c: 00 00 +# DIS-NEXT: 100105e: 00 00 +# DIS-NEXT: 1001060: c0 10 00 00 10 48 larl %r1, 0x10030f0 +# DIS-NEXT: 1001066: e3 10 10 00 00 04 lg %r1, 0(%r1) +# DIS-NEXT: 100106c: 07 f1 br %r1 +# DIS-NEXT: 100106e: 0d 10 basr %r1, 0 +# DIS-NEXT: 1001070: e3 10 10 0c 00 14 lgf %r1, 12(%r1) +# DIS-NEXT: 1001076: c0 f4 ff ff ff d5 jg 0x1001020 +# DIS-NEXT: 100107c: 00 00 +# DIS-NEXT: 100107e: 00 18 + +.global _start, foo, bar +.weak weak + +_start: + ## Use @plt to avoid generating direct references that would force + ## allocation of a canonical PLT entry. + brasl %r14, foo@plt + brasl %r14, bar@plt + brasl %r14, weak@plt + +## foo is local and non-preemptable, no PLT is generated. +foo: + br %r14 diff --git a/lld/test/ELF/systemz-reloc-abs.s b/lld/test/ELF/systemz-reloc-abs.s new file mode 100644 index 00000000000000..3a01ed7df3519a --- /dev/null +++ b/lld/test/ELF/systemz-reloc-abs.s @@ -0,0 +1,29 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=s390x %S/Inputs/abs255.s -o %t255.o +# RUN: llvm-mc -filetype=obj -triple=s390x %S/Inputs/abs256.s -o %t256.o +# RUN: llvm-mc -filetype=obj -triple=s390x %S/Inputs/abs257.s -o %t257.o + +# RUN: ld.lld %t.o %t256.o -o %t +# RUN: llvm-readelf -x .data %t | FileCheck %s +# CHECK: 0x{{[0-9a-f]+}} ff80ffff 8000 + +# RUN: not ld.lld %t.o %t255.o -o /dev/null 2>&1 | FileCheck --check-prefix=OVERFLOW1 %s +# OVERFLOW1: relocation R_390_8 out of range: -129 is not in [-128, 255] +# OVERFLOW1: relocation R_390_16 out of range: -32769 is not in [-32768, 65535] +# OVERFLOW1: relocation R_390_32 out of range: -2147483649 is not in [-2147483648, 4294967295] + +# RUN: not ld.lld %t.o %t257.o -o /dev/null 2>&1 | FileCheck --check-prefix=OVERFLOW2 %s +# OVERFLOW2: relocation R_390_8 out of range: 256 is not in [-128, 255] +# OVERFLOW2: relocation R_390_16 out of range: 65536 is not in [-32768, 65535] +# OVERFLOW2: relocation R_390_32 out of range: 4294967296 is not in [-2147483648, 4294967295] + +.globl _start +_start: +.data +.byte foo - 1 +.byte foo - 384 +.word foo + 0xfeff +.word foo - 0x8100 +.long foo + 0xfffffeff +.long foo - 0x80000100 diff --git a/lld/test/ELF/systemz-reloc-disp12.s b/lld/test/ELF/systemz-reloc-disp12.s new file mode 100644 index 00000000000000..3d32707d149fe7 --- /dev/null +++ b/lld/test/ELF/systemz-reloc-disp12.s @@ -0,0 +1,21 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym DISP=291 %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym DISP=4095 %s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym DISP=4096 %s -o %t3.o + +# RUN: ld.lld --section-start=.text=0x0 %t1.o -o %t1out +# RUN: ld.lld --section-start=.text=0x0 %t2.o -o %t2out +# RUN: not ld.lld --section-start=.text=0x0 %t3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix RANGE + +# RANGE: relocation R_390_12 out of range: 4096 is not in [0, 4095] + +# RUN: llvm-readelf --hex-dump=.text %t1out | FileCheck %s -DINSN=58678123 --check-prefix DUMP +# RUN: llvm-readelf --hex-dump=.text %t2out | FileCheck %s -DINSN=58678fff --check-prefix DUMP + +# DUMP: 0x00000000 [[INSN]] + +.text +.globl _start +_start: + .reloc .+2, R_390_12, DISP + l %r6, 0(%r7,%r8) diff --git a/lld/test/ELF/systemz-reloc-disp20.s b/lld/test/ELF/systemz-reloc-disp20.s new file mode 100644 index 00000000000000..88cd657c6ae3cb --- /dev/null +++ b/lld/test/ELF/systemz-reloc-disp20.s @@ -0,0 +1,21 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym DISP=74565 %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym DISP=524287 %s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym DISP=524288 %s -o %t3.o + +# RUN: ld.lld --section-start=.text=0x0 %t1.o -o %t1out +# RUN: ld.lld --section-start=.text=0x0 %t2.o -o %t2out +# RUN: not ld.lld --section-start=.text=0x0 %t3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix RANGE + +# RANGE: relocation R_390_20 out of range: 524288 is not in [-524288, 524287] + +# RUN: llvm-readelf --hex-dump=.text %t1out | FileCheck %s -DINSN="e3678345 1204" --check-prefix DUMP +# RUN: llvm-readelf --hex-dump=.text %t2out | FileCheck %s -DINSN="e3678fff 7f04" --check-prefix DUMP + +# DUMP: 0x00000000 [[INSN]] + +.text +.globl _start +_start: + .reloc .+2, R_390_20, DISP + lg %r6, 0(%r7,%r8) diff --git a/lld/test/ELF/systemz-reloc-pc16.s b/lld/test/ELF/systemz-reloc-pc16.s new file mode 100644 index 00000000000000..4e646bdc0cb996 --- /dev/null +++ b/lld/test/ELF/systemz-reloc-pc16.s @@ -0,0 +1,39 @@ +# REQUIRES: systemz +# RUN: rm -rf %t && split-file %s %t + +## Check recompile with -fPIC error message +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %t/shared.s -o %t/shared.o +# RUN: not ld.lld -shared %t/shared.o -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: relocation R_390_PC16 cannot be used against symbol '_shared'; recompile with -fPIC +# CHECK: >>> defined in {{.*}} +# CHECK: >>> referenced by {{.*}}:(.data+0x1) + +## Check patching of negative addends + +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym ADDEND=1 %t/addend.s -o %t/1.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym ADDEND=32768 %t/addend.s -o %t/2.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym ADDEND=32769 %t/addend.s -o %t/3.o + +# RUN: ld.lld --section-start=.text=0x0 %t/1.o -o %t/1out +# RUN: ld.lld --section-start=.text=0x0 %t/2.o -o %t/2out +# RUN: not ld.lld --section-start=.text=0x0 %t/3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix RANGE + +# RANGE: relocation R_390_PC16 out of range + +# RUN: llvm-readelf --hex-dump=.text %t/1out | FileCheck %s -DADDEND=ffff --check-prefix DUMP +# RUN: llvm-readelf --hex-dump=.text %t/2out | FileCheck %s -DADDEND=8000 --check-prefix DUMP + +# DUMP: 0x00000000 [[ADDEND]] + +#--- shared.s +.data + .byte 0xe8 + .word _shared - . + +#--- addend.s +.text +.globl _start +_start: + .reloc ., R_390_PC16, .text-ADDEND + .space 2 diff --git a/lld/test/ELF/systemz-reloc-pc32.s b/lld/test/ELF/systemz-reloc-pc32.s new file mode 100644 index 00000000000000..49ff0bd3baa89f --- /dev/null +++ b/lld/test/ELF/systemz-reloc-pc32.s @@ -0,0 +1,39 @@ +# REQUIRES: systemz +# RUN: rm -rf %t && split-file %s %t + +## Check recompile with -fPIC error message +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %t/shared.s -o %t/shared.o +# RUN: not ld.lld -shared %t/shared.o -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: relocation R_390_PC32 cannot be used against symbol '_shared'; recompile with -fPIC +# CHECK: >>> defined in {{.*}} +# CHECK: >>> referenced by {{.*}}:(.data+0x1) + +## Check patching of negative addends + +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym ADDEND=1 %t/addend.s -o %t/1.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym ADDEND=2147483648 %t/addend.s -o %t/2.o +# RUN: llvm-mc -filetype=obj -triple=s390x -defsym ADDEND=2147483649 %t/addend.s -o %t/3.o + +# RUN: ld.lld --section-start=.text=0x0 %t/1.o -o %t/1out +# RUN: ld.lld --section-start=.text=0x0 %t/2.o -o %t/2out +# RUN: not ld.lld --section-start=.text=0x0 %t/3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix RANGE + +# RANGE: relocation R_390_PC32 out of range + +# RUN: llvm-readelf --hex-dump=.text %t/1out | FileCheck %s -DADDEND=ffffffff --check-prefix DUMP +# RUN: llvm-readelf --hex-dump=.text %t/2out | FileCheck %s -DADDEND=80000000 --check-prefix DUMP + +# DUMP: 0x00000000 [[ADDEND]] + +#--- shared.s +.data + .byte 0xe8 + .long _shared - . + +#--- addend.s +.text +.globl _start +_start: + .reloc ., R_390_PC32, .text-ADDEND + .space 4 diff --git a/lld/test/ELF/systemz-reloc-range12.s b/lld/test/ELF/systemz-reloc-range12.s new file mode 100644 index 00000000000000..571ba60ee78442 --- /dev/null +++ b/lld/test/ELF/systemz-reloc-range12.s @@ -0,0 +1,16 @@ +// REQUIRES: systemz +// RUN: llvm-mc %s -o %t.o -triple s390x-unknown-linux -mcpu=z13 -filetype=obj +// RUN: not ld.lld %t.o -o /dev/null -shared 2>&1 | FileCheck %s +// RUN: ld.lld --noinhibit-exec -shared %t.o -o %t 2>&1 | FileCheck %s +// RUN: ls %t + +// CHECK: {{.*}}:(.text+0x1): relocation R_390_PC12DBL out of range: 4096 is not in [-4096, 4095] +// CHECK-NOT: relocation + + bprp 1,foo,0 + bprp 1,foo,0 + + .hidden foo + .section .text.foo + .zero 0xff4 +foo: diff --git a/lld/test/ELF/systemz-reloc-range16.s b/lld/test/ELF/systemz-reloc-range16.s new file mode 100644 index 00000000000000..201eaead43fc83 --- /dev/null +++ b/lld/test/ELF/systemz-reloc-range16.s @@ -0,0 +1,16 @@ +// REQUIRES: systemz +// RUN: llvm-mc %s -o %t.o -triple s390x-unknown-linux -filetype=obj +// RUN: not ld.lld %t.o -o /dev/null -shared 2>&1 | FileCheck %s +// RUN: ld.lld --noinhibit-exec -shared %t.o -o %t 2>&1 | FileCheck %s +// RUN: ls %t + +// CHECK: {{.*}}:(.text+0x2): relocation R_390_PC16DBL out of range: 65536 is not in [-65536, 65535] +// CHECK-NOT: relocation + + j foo + j foo + + .hidden foo + .bss + .zero 0xdf88 +foo: diff --git a/lld/test/ELF/systemz-reloc-range24.s b/lld/test/ELF/systemz-reloc-range24.s new file mode 100644 index 00000000000000..315fdff8a4c098 --- /dev/null +++ b/lld/test/ELF/systemz-reloc-range24.s @@ -0,0 +1,16 @@ +// REQUIRES: systemz +// RUN: llvm-mc %s -o %t.o -triple s390x-unknown-linux -mcpu=z13 -filetype=obj +// RUN: not ld.lld %t.o -o /dev/null -shared 2>&1 | FileCheck %s +// RUN: ld.lld --noinhibit-exec -shared %t.o -o %t 2>&1 | FileCheck %s +// RUN: ls %t + +// CHECK: {{.*}}:(.text+0x3): relocation R_390_PC24DBL out of range: 16777216 is not in [-16777216, 16777215] +// CHECK-NOT: relocation + + bprp 1,0,foo + bprp 1,0,foo + + .hidden foo + .section .text.foo + .zero 0xfffff4 +foo: diff --git a/lld/test/ELF/systemz-reloc-range32.s b/lld/test/ELF/systemz-reloc-range32.s new file mode 100644 index 00000000000000..215d36f386f7bb --- /dev/null +++ b/lld/test/ELF/systemz-reloc-range32.s @@ -0,0 +1,16 @@ +// REQUIRES: systemz +// RUN: llvm-mc %s -o %t.o -triple s390x-unknown-linux -filetype=obj +// RUN: not ld.lld %t.o -o /dev/null -shared 2>&1 | FileCheck %s +// RUN: ld.lld --noinhibit-exec -shared %t.o -o %t 2>&1 | FileCheck %s +// RUN: ls %t + +// CHECK: {{.*}}:(.text+0x2): relocation R_390_PC32DBL out of range: 4294967296 is not in [-4294967296, 4294967295] +// CHECK-NOT: relocation + + larl %r0, foo + larl %r0, foo + + .hidden foo + .bss + .zero 0xffffdf80 +foo: diff --git a/lld/test/ELF/systemz-shared.s b/lld/test/ELF/systemz-shared.s new file mode 100644 index 00000000000000..cff03e0e0b1d45 --- /dev/null +++ b/lld/test/ELF/systemz-shared.s @@ -0,0 +1,86 @@ +// REQUIRES: systemz +// RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %p/Inputs/shared.s -o %t2.o +// RUN: ld.lld -shared %t2.o -soname=%t2.so -o %t2.so +// RUN: llvm-readelf -S %t2.so | FileCheck --check-prefix=SO %s + +// RUN: ld.lld -dynamic-linker /lib/ld64.so.1 -rpath foo -rpath bar --export-dynamic %t.o %t2.so -o %t +// RUN: llvm-readelf -S -s -l --dynamic %t | FileCheck %s + +// RUN: ld.lld %t.o %t2.so %t2.so -o %t2 +// RUN: llvm-readelf --dyn-syms %t2 | FileCheck --check-prefix=DONT_EXPORT %s + +// Make sure .symtab is properly aligned. +// SO: .symtab SYMTAB 0000000000000000 {{[^ ]*}} {{[^ ]*}} {{[^ ]*}} {{[^ ]*}} {{[^ ]*}} 8 + +// CHECK: Section Headers: +// CHECK-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +// CHECK-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +// CHECK-NEXT: [ 1] .interp PROGBITS 0000000001000200 000200 00000f 00 A 0 0 1 +// CHECK-NEXT: [ 2] .dynsym DYNSYM 0000000001000210 000210 000060 18 A 5 1 8 +// CHECK-NEXT: [ 3] .gnu.hash GNU_HASH 0000000001000270 000270 000020 00 A 2 0 8 +// CHECK-NEXT: [ 4] .hash HASH 0000000001000290 000290 000028 04 A 2 0 4 +// CHECK-NEXT: [ 5] .dynstr STRTAB 00000000010002b8 0002b8 {{.*}} 00 A 0 0 1 +// CHECK-NEXT: [ 6] .rela.dyn RELA {{0*}}[[RELADYN:.*]] {{.*}} 000030 18 A 2 0 8 +// CHECK-NEXT: [ 7] .text PROGBITS {{0*}}[[TEXT:.*]] {{.*}} 00000c 00 AX 0 0 4 +// CHECK-NEXT: [ 8] .dynamic DYNAMIC {{0*}}[[DYNAMIC:.*]] {{.*}} 0000d0 10 WA 5 0 8 +// CHECK-NEXT: [ 9] .got PROGBITS {{.*}} {{.*}} 000028 00 WA 0 0 8 +// CHECK-NEXT: [10] .relro_padding NOBITS {{.*}} {{.*}} {{.*}} 00 WA 0 0 1 +// CHECK-NEXT: [11] .comment PROGBITS 0000000000000000 {{.*}} 000008 01 MS 0 0 1 +// CHECK-NEXT: [12] .symtab SYMTAB 0000000000000000 {{.*}} {{.*}} 18 14 2 8 +// CHECK-NEXT: [13] .shstrtab STRTAB 0000000000000000 {{.*}} {{.*}} 00 0 0 1 +// CHECK-NEXT: [14] .strtab STRTAB 0000000000000000 {{.*}} {{.*}} 00 0 0 1 + +// CHECK: Program Headers: +// CHECK-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +// CHECK-NEXT: PHDR 0x000040 0x0000000001000040 0x0000000001000040 0x0001c0 0x0001c0 R 0x8 +// CHECK-NEXT: INTERP 0x000200 0x0000000001000200 0x0000000001000200 0x00000f 0x00000f R 0x1 +// CHECK-NEXT: [Requesting program interpreter: /lib/ld64.so.1] +// CHECK-NEXT: LOAD 0x000000 0x0000000001000000 0x0000000001000000 0x{{.*}} 0x{{.*}} R 0x1000 +// CHECK-NEXT: LOAD 0x{{.*}} 0x{{0*}}[[TEXT]] 0x{{0*}}[[TEXT]] 0x00000c 0x00000c R E 0x1000 +// CHECK-NEXT: LOAD 0x{{.*}} 0x{{0*}}[[DYNAMIC]] 0x{{0*}}[[DYNAMIC]] 0x{{.*}} 0x{{.*}} RW 0x1000 +// CHECK-NEXT: DYNAMIC 0x{{.*}} 0x{{0*}}[[DYNAMIC]] 0x{{0*}}[[DYNAMIC]] 0x0000d0 0x0000d0 RW 0x8 +// CHECK-NEXT: GNU_RELRO 0x{{.*}} 0x{{0*}}[[DYNAMIC]] 0x{{0*}}[[DYNAMIC]] 0x{{.*}} 0x{{.*}} R 0x1 +// CHECK-NEXT: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x0 + +// CHECK: Dynamic section at offset {{.*}} contains 13 entries: +// CHECK-NEXT: Tag Type Name/Value +// CHECK-NEXT: 0x000000000000001d (RUNPATH) Library runpath: [foo:bar] +// CHECK-NEXT: 0x0000000000000001 (NEEDED) Shared library: [{{.*}}2.so] +// CHECK-NEXT: 0x0000000000000015 (DEBUG) 0x0 +// CHECK-NEXT: 0x0000000000000007 (RELA) 0x[[RELADYN]] +// CHECK-NEXT: 0x0000000000000008 (RELASZ) 48 (bytes) +// CHECK-NEXT: 0x0000000000000009 (RELAENT) 24 (bytes) +// CHECK-NEXT: 0x0000000000000006 (SYMTAB) 0x1000210 +// CHECK-NEXT: 0x000000000000000b (SYMENT) 24 (bytes) +// CHECK-NEXT: 0x0000000000000005 (STRTAB) 0x10002b8 +// CHECK-NEXT: 0x000000000000000a (STRSZ) {{.*}} (bytes) +// CHECK-NEXT: 0x000000006ffffef5 (GNU_HASH) 0x +// CHECK-NEXT: 0x0000000000000004 (HASH) 0x +// CHECK-NEXT: 0x0000000000000000 (NULL) 0x0 + +// CHECK: Symbol table '.dynsym' contains 4 entries: +// CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK-NEXT: 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bar +// CHECK-NEXT: 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND zed +// CHECK-NEXT: 3: {{.*}} 0 NOTYPE GLOBAL DEFAULT 7 _start + +// CHECK: Symbol table '.symtab' contains 5 entries: +// CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name +// CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +// CHECK-NEXT: 1: {{0*}}[[DYNAMIC]] 0 NOTYPE LOCAL HIDDEN 8 _DYNAMIC +// CHECK-NEXT: 2: {{.*}} 0 NOTYPE GLOBAL DEFAULT 7 _start +// CHECK-NEXT: 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bar +// CHECK-NEXT: 4: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND zed + +// DONT_EXPORT: Symbol table '.dynsym' contains 3 entries: +// DONT_EXPORT-NEXT: Num: Value Size Type Bind Vis Ndx Name +// DONT_EXPORT-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +// DONT_EXPORT-NEXT: 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bar +// DONT_EXPORT-NEXT: 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND zed + +.global _start +_start: + lgrl %r1,bar@GOT + lgrl %r2,zed@GOT diff --git a/lld/test/ELF/systemz-tls-gd.s b/lld/test/ELF/systemz-tls-gd.s new file mode 100644 index 00000000000000..3d9fe4049b7191 --- /dev/null +++ b/lld/test/ELF/systemz-tls-gd.s @@ -0,0 +1,142 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o +# RUN: echo '.tbss; .globl b, c; b: .zero 4; c:' | llvm-mc -filetype=obj -triple=s390x-unknown-linux - -o %t1.o +# RUN: ld.lld -shared -soname=t1.so %t1.o -o %t1.so + +# RUN: ld.lld -shared %t.o %t1.o -o %t.so +# RUN: llvm-readelf -r %t.so | FileCheck --check-prefix=GD-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=GD %s +# RUN: llvm-objdump --section .data.rel.ro --full-contents %t.so | FileCheck --check-prefix=GD-DATA %s + +# RUN: ld.lld %t.o %t1.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s +# RUN: llvm-objdump --section .data.rel.ro --full-contents %t | FileCheck --check-prefix=LE-DATA %s + +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=IE-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=IE %s +# RUN: llvm-objdump --section .data.rel.ro --full-contents %t | FileCheck --check-prefix=IE-DATA %s + +# GD-REL: Relocation section '.rela.dyn' at offset {{.*}} contains 6 entries: +# GD-REL: 0000000000002570 0000000200000036 R_390_TLS_DTPMOD 0000000000000008 a + 0 +# GD-REL: 0000000000002578 0000000200000037 R_390_TLS_DTPOFF 0000000000000008 a + 0 +# GD-REL: 0000000000002580 0000000300000036 R_390_TLS_DTPMOD 000000000000000c b + 0 +# GD-REL: 0000000000002588 0000000300000037 R_390_TLS_DTPOFF 000000000000000c b + 0 +# GD-REL: 0000000000002590 0000000400000036 R_390_TLS_DTPMOD 0000000000000010 c + 0 +# GD-REL: 0000000000002598 0000000400000037 R_390_TLS_DTPOFF 0000000000000010 c + 0 + +## _GLOBAL_OFFSET_TABLE is at 0x2558 +# GD: larl %r12, 0x2558 + +## GOT offset of the TLS descriptor for a is at 0x2460 +# GD-NEXT: lgrl %r2, 0x2460 +# GD-NEXT: brasl %r14, 0x1440 +# GD-NEXT: lgf %r2, 0(%r2,%r7) + +## GOT offset of the TLS descriptor for b is at 0x2468 +# GD-NEXT: lgrl %r2, 0x2468 +# GD-NEXT: brasl %r14, 0x1440 +# GD-NEXT: lgf %r2, 0(%r2,%r7) + +## GOT offset of the TLS descriptor for c is at 0x2470 +# GD-NEXT: lgrl %r2, 0x2470 +# GD-NEXT: brasl %r14, 0x1440 +# GD-NEXT: lgf %r2, 0(%r2,%r7) + +## TLS descriptor addresses / GOT offsets: +# a: 0x2570 / 0x18 +# b: 0x2580 / 0x28 +# c: 0x2590 / 0x38 +# GD-DATA: 2460 00000000 00000018 00000000 00000028 +# GD-DATA: 2470 00000000 00000038 + +# NOREL: no relocations + +## _GLOBAL_OFFSET_TABLE is at 0x1002230 +# LE: larl %r12, 0x1002230 + +## TP offset of a is at 0x1002218 +# LE-NEXT: lgrl %r2, 0x1002218 +# LE-NEXT: brcl 0, +# LE-NEXT: lgf %r2, 0(%r2,%r7) + +## TP offset of b is at 0x1002220 +# LE-NEXT: lgrl %r2, 0x1002220 +# LE-NEXT: brcl 0, +# LE-NEXT: lgf %r2, 0(%r2,%r7) + +## TP offset of c is at 0x1002228 +# LE-NEXT: lgrl %r2, 0x1002228 +# LE-NEXT: brcl 0, +# LE-NEXT: lgf %r2, 0(%r2,%r7) + +## TP offsets +# a: -8 +# b: -4 +# c: 0 +# LE-DATA: 1002218 ffffffff fffffff8 ffffffff fffffffc +# LE-DATA: 1002228 00000000 00000000 + + +# IE-REL: Relocation section '.rela.dyn' at offset {{.*}} contains 2 entries: +# IE-REL: 0000000001002430 0000000200000038 R_390_TLS_TPOFF 0000000000000000 b + 0 +# IE-REL: 0000000001002438 0000000300000038 R_390_TLS_TPOFF 0000000000000000 c + 0 + +## _GLOBAL_OFFSET_TABLE is at 0x1002418 +# IE: larl %r12, 0x1002418 + +## TP offset of a is at 0x1002340 +# IE-NEXT: lgrl %r2, 0x1002340 +# IE-NEXT: brcl 0, +# IE-NEXT: lgf %r2, 0(%r2,%r7) + +## GOT offset of the TP offset for b is at 0x1002348 +# IE-NEXT: lgrl %r2, 0x1002348 +# IE-NEXT: lg %r2, 0(%r2,%r12) +# IE-NEXT: lgf %r2, 0(%r2,%r7) + +## GOT offset of the TP offset for c is at 0x1002350 +# IE-NEXT: lgrl %r2, 0x1002350 +# IE-NEXT: lg %r2, 0(%r2,%r12) +# IE-NEXT: lgf %r2, 0(%r2,%r7) + +## TP offsets (a) / GOT offset of TP offsets (b, c) +# a: -4 +# b: 0x1002430 / 0x18 +# c: 0x1002438 / 0x20 +# IE-DATA: 1002340 ffffffff fffffffc 00000000 00000018 +# IE-DATA: 1002350 00000000 00000020 + + +ear %r7,%a0 +sllg %r7,%r1,32 +ear %r7,%a1 +larl %r12,_GLOBAL_OFFSET_TABLE_ + +lgrl %r2,.LC0 +brasl %r14,__tls_get_offset@PLT:tls_gdcall:a +lgf %r2,0(%r2,%r7) + +lgrl %r2,.LC1 +brasl %r14,__tls_get_offset@PLT:tls_gdcall:b +lgf %r2,0(%r2,%r7) + +lgrl %r2,.LC2 +brasl %r14,__tls_get_offset@PLT:tls_gdcall:c +lgf %r2,0(%r2,%r7) + + .section .data.rel.ro,"aw" + .align 8 +.LC0: + .quad a@TLSGD +.LC1: + .quad b@TLSGD +.LC2: + .quad c@TLSGD + + .section .tbss + .globl a + .zero 8 +a: + .zero 4 diff --git a/lld/test/ELF/systemz-tls-ie.s b/lld/test/ELF/systemz-tls-ie.s new file mode 100644 index 00000000000000..44cbef99ab26cb --- /dev/null +++ b/lld/test/ELF/systemz-tls-ie.s @@ -0,0 +1,74 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o + +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: llvm-readelf -r %t.so | FileCheck --check-prefix=IE-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=IE %s + +# RUN: ld.lld %t.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s +# RUN: llvm-objdump --section .got --full-contents %t | FileCheck --check-prefix=LE-GOT %s + +# IE-REL: Relocation section '.rela.dyn' at offset {{.*}} contains 3 entries: +# IE-REL: 0000000000002400 0000000100000038 R_390_TLS_TPOFF 0000000000000008 a + 0 +# IE-REL: 0000000000002408 0000000200000038 R_390_TLS_TPOFF 000000000000000c b + 0 +# IE-REL: 0000000000002410 0000000300000038 R_390_TLS_TPOFF 0000000000000010 c + 0 + +## TP offset for a is at 0x2400 +# IE: lgrl %r1, 0x2400 +# IE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offset for b is at 0x2408 +# IE-NEXT: lgrl %r1, 0x2408 +# IE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offset for c is at 0x2410 +# IE-NEXT: lgrl %r1, 0x2410 +# IE-NEXT: lgf %r1, 0(%r1,%r7) + +# NOREL: no relocations + +## TP offset for a is at 0x1002218 +# LE: lgrl %r1, 0x1002218 +# LE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offset for b is at 0x1002220 +# LE-NEXT: lgrl %r1, 0x1002220 +# LE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offset for c is at 0x1002228 +# LE-NEXT: lgrl %r1, 0x1002228 +# LE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offsets in GOT: +# a: -8 +# b: -4 +# c: 0 +# LE-GOT: 1002200 00000000 00000000 00000000 00000000 +# LE-GOT: 1002210 00000000 00000000 ffffffff fffffff8 +# LE-GOT: 1002220 ffffffff fffffffc 00000000 00000000 + +ear %r7,%a0 +sllg %r7,%r1,32 +ear %r7,%a1 + +lgrl %r1, a@indntpoff +lgf %r1,0(%r1,%r7) + +lgrl %r1, b@indntpoff +lgf %r1,0(%r1,%r7) + +lgrl %r1, c@indntpoff +lgf %r1,0(%r1,%r7) + + .section .tbss + .globl a + .globl b + .globl c + .zero 8 +a: + .zero 4 +b: + .zero 4 +c: diff --git a/lld/test/ELF/systemz-tls-ld.s b/lld/test/ELF/systemz-tls-ld.s new file mode 100644 index 00000000000000..600ca205c3759e --- /dev/null +++ b/lld/test/ELF/systemz-tls-ld.s @@ -0,0 +1,114 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o + +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: llvm-readelf -r %t.so | FileCheck --check-prefix=LD-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=LD %s +# RUN: llvm-objdump --section .data.rel.ro --full-contents %t.so | FileCheck --check-prefix=LD-DATA %s + +# RUN: ld.lld %t.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s +# RUN: llvm-objdump --section .data.rel.ro --full-contents %t | FileCheck --check-prefix=LE-DATA %s + +# LD-REL: Relocation section '.rela.dyn' at offset {{.*}} contains 1 entries: +# LD-REL: 00000000000024f8 0000000000000036 R_390_TLS_DTPMOD 0 + +## _GLOBAL_OFFSET_TABLE is at 0x24e0 +# LD: larl %r12, 0x24e0 + +## GOT offset of the LDM TLS descriptor is at 0x23e0 +# LD-NEXT: lgrl %r2, 0x23e0 +# LD-NEXT: brasl %r14, 0x13c0 +# LD-NEXT: la %r2, 0(%r2,%r7) + +## DTP offset for a is at 0x23e8 +# LD-NEXT: lgrl %r1, 0x23e8 +# LD-NEXT: lgf %r1, 0(%r1,%r2) + +## DTP offset for b is at 0x23f0 +# LD-NEXT: lgrl %r1, 0x23f0 +# LD-NEXT: lgf %r1, 0(%r1,%r2) + +## DTP offset for c is at 0x23f8 +# LD-NEXT: lgrl %r1, 0x23f8 +# LD-NEXT: lgf %r1, 0(%r1,%r2) + +## LDM GOT offset / DTP offsets: +# LDM TLS: 0x24f8 / 0x18 +# a: 8 +# b: 12 +# c: 16 +# LD-DATA: 23e0 00000000 00000018 00000000 00000008 +# LD-DATA: 23f0 00000000 0000000c 00000000 00000010 + +# NOREL: no relocations + +## _GLOBAL_OFFSET_TABLE is at 0x1002230 +# LE: larl %r12, 0x1002230 + +## GOT offset of the LDM TLS descriptor is at 0x1002210 +# LE-NEXT: lgrl %r2, 0x1002210 +# LE-NEXT: brcl 0, +# LE-NEXT: la %r2, 0(%r2,%r7) + +## TP offset for a is at 0x1002218 +# LE-NEXT: lgrl %r1, 0x1002218 +# LE-NEXT: lgf %r1, 0(%r1,%r2) + +## TP offset for b is at 0x1002220 +# LE-NEXT: lgrl %r1, 0x1002220 +# LE-NEXT: lgf %r1, 0(%r1,%r2) + +## TP offset for c is at 0x1002228 +# LE-NEXT: lgrl %r1, 0x1002228 +# LE-NEXT: lgf %r1, 0(%r1,%r2) + +## zeroed LDM / TP offsets: +# LDM TLS: 0 +# a: -8 +# b: -4 +# c: 0 +# LE-DATA: 1002210 00000000 00000000 ffffffff fffffff8 +# LE-DATA: 1002220 ffffffff fffffffc 00000000 00000000 + + +ear %r7,%a0 +sllg %r7,%r1,32 +ear %r7,%a1 +larl %r12,_GLOBAL_OFFSET_TABLE_ + +lgrl %r2,.LC0 +brasl %r14,__tls_get_offset@PLT:tls_ldcall:a +la %r2,0(%r2,%r7) + +lgrl %r1, .LC1 +lgf %r1,0(%r1,%r2) + +lgrl %r1, .LC2 +lgf %r1,0(%r1,%r2) + +lgrl %r1, .LC3 +lgf %r1,0(%r1,%r2) + + .section .data.rel.ro,"aw" + .align 8 +.LC0: + .quad a@TLSLDM +.LC1: + .quad a@DTPOFF +.LC2: + .quad b@DTPOFF +.LC3: + .quad c@DTPOFF + + .section .tbss + .globl a + .globl b + .globl c + .zero 8 +a: + .zero 4 +b: + .zero 4 +c: diff --git a/lld/test/ELF/systemz-tls-le.s b/lld/test/ELF/systemz-tls-le.s new file mode 100644 index 00000000000000..9e41fc768da391 --- /dev/null +++ b/lld/test/ELF/systemz-tls-le.s @@ -0,0 +1,61 @@ +# REQUIRES: systemz +# RUN: llvm-mc -filetype=obj -triple=s390x-unknown-linux %s -o %t.o + +# RUN: ld.lld %t.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s +# RUN: llvm-objdump --section .data.rel.ro --full-contents %t | FileCheck --check-prefix=LE-DATA %s + +# NOREL: no relocations + +## TP offset for a is at 0x1002200 +# LE: lgrl %r1, 0x1002200 +# LE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offset for b is at 0x1002208 +# LE-NEXT: lgrl %r1, 0x1002208 +# LE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offset for c is at 0x1002210 +# LE-NEXT: lgrl %r1, 0x1002210 +# LE-NEXT: lgf %r1, 0(%r1,%r7) + +## TP offsets: +# a: -8 +# b: -4 +# c: 0 +# LE-DATA: 1002200 ffffffff fffffff8 ffffffff fffffffc +# LE-DATA: 1002210 00000000 00000000 + +ear %r7,%a0 +sllg %r7,%r1,32 +ear %r7,%a1 + +lgrl %r1, .LC0 +lgf %r1,0(%r1,%r7) + +lgrl %r1, .LC1 +lgf %r1,0(%r1,%r7) + +lgrl %r1, .LC2 +lgf %r1,0(%r1,%r7) + + .section .data.rel.ro,"aw" + .align 8 +.LC0: + .quad a@ntpoff +.LC1: + .quad b@ntpoff +.LC2: + .quad c@ntpoff + + .section .tbss + .globl a + .globl b + .globl c + .zero 8 +a: + .zero 4 +b: + .zero 4 +c: diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py index b3e07f1f823cc4..d309c2ad4ee284 100644 --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -83,6 +83,7 @@ "PowerPC": "ppc", "RISCV": "riscv", "Sparc": "sparc", + "SystemZ": "systemz", "WebAssembly": "wasm", "X86": "x86", },