Path: blob/main/contrib/llvm-project/lld/ELF/Arch/SystemZ.cpp
34878 views
//===- SystemZ.cpp --------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "OutputSections.h"9#include "Symbols.h"10#include "SyntheticSections.h"11#include "Target.h"12#include "lld/Common/ErrorHandler.h"13#include "llvm/BinaryFormat/ELF.h"14#include "llvm/Support/Endian.h"1516using namespace llvm;17using namespace llvm::support::endian;18using namespace llvm::ELF;19using namespace lld;20using namespace lld::elf;2122namespace {23class SystemZ : public TargetInfo {24public:25SystemZ();26int getTlsGdRelaxSkip(RelType type) const override;27RelExpr getRelExpr(RelType type, const Symbol &s,28const uint8_t *loc) const override;29RelType getDynRel(RelType type) const override;30void writeGotHeader(uint8_t *buf) const override;31void writeGotPlt(uint8_t *buf, const Symbol &s) const override;32void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;33void writePltHeader(uint8_t *buf) const override;34void addPltHeaderSymbols(InputSection &isd) const override;35void writePlt(uint8_t *buf, const Symbol &sym,36uint64_t pltEntryAddr) const override;37RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;38RelExpr adjustGotPcExpr(RelType type, int64_t addend,39const uint8_t *loc) const override;40bool relaxOnce(int pass) const override;41void relocate(uint8_t *loc, const Relocation &rel,42uint64_t val) const override;43int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;4445private:46void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;47void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;48void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;49void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;50};51} // namespace5253SystemZ::SystemZ() {54copyRel = R_390_COPY;55gotRel = R_390_GLOB_DAT;56pltRel = R_390_JMP_SLOT;57relativeRel = R_390_RELATIVE;58iRelativeRel = R_390_IRELATIVE;59symbolicRel = R_390_64;60tlsGotRel = R_390_TLS_TPOFF;61tlsModuleIndexRel = R_390_TLS_DTPMOD;62tlsOffsetRel = R_390_TLS_DTPOFF;63gotHeaderEntriesNum = 3;64gotPltHeaderEntriesNum = 0;65gotEntrySize = 8;66pltHeaderSize = 32;67pltEntrySize = 32;68ipltEntrySize = 32;6970// This "trap instruction" is used to fill gaps between sections.71// On SystemZ, the behavior of the GNU ld is to fill those gaps72// with nop instructions instead - and unfortunately the default73// glibc crt object files (used to) rely on that behavior since74// they use an alignment on the .init section fragments that causes75// gaps which must be filled with nops as they are being executed.76// Therefore, we provide a nop instruction as "trapInstr" here.77trapInstr = {0x07, 0x07, 0x07, 0x07};7879defaultImageBase = 0x1000000;80}8182RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s,83const uint8_t *loc) const {84switch (type) {85case R_390_NONE:86return R_NONE;87// Relocations targeting the symbol value.88case R_390_8:89case R_390_12:90case R_390_16:91case R_390_20:92case R_390_32:93case R_390_64:94return R_ABS;95case R_390_PC16:96case R_390_PC32:97case R_390_PC64:98case R_390_PC12DBL:99case R_390_PC16DBL:100case R_390_PC24DBL:101case R_390_PC32DBL:102return R_PC;103case R_390_GOTOFF16:104case R_390_GOTOFF: // a.k.a. R_390_GOTOFF32105case R_390_GOTOFF64:106return R_GOTREL;107// Relocations targeting the PLT associated with the symbol.108case R_390_PLT32:109case R_390_PLT64:110case R_390_PLT12DBL:111case R_390_PLT16DBL:112case R_390_PLT24DBL:113case R_390_PLT32DBL:114return R_PLT_PC;115case R_390_PLTOFF16:116case R_390_PLTOFF32:117case R_390_PLTOFF64:118return R_PLT_GOTREL;119// Relocations targeting the GOT entry associated with the symbol.120case R_390_GOTENT:121return R_GOT_PC;122case R_390_GOT12:123case R_390_GOT16:124case R_390_GOT20:125case R_390_GOT32:126case R_390_GOT64:127return R_GOT_OFF;128// Relocations targeting the GOTPLT entry associated with the symbol.129case R_390_GOTPLTENT:130return R_GOTPLT_PC;131case R_390_GOTPLT12:132case R_390_GOTPLT16:133case R_390_GOTPLT20:134case R_390_GOTPLT32:135case R_390_GOTPLT64:136return R_GOTPLT_GOTREL;137// Relocations targeting _GLOBAL_OFFSET_TABLE_.138case R_390_GOTPC:139case R_390_GOTPCDBL:140return R_GOTONLY_PC;141// TLS-related relocations.142case R_390_TLS_LOAD:143return R_NONE;144case R_390_TLS_GDCALL:145return R_TLSGD_PC;146case R_390_TLS_LDCALL:147return R_TLSLD_PC;148case R_390_TLS_GD32:149case R_390_TLS_GD64:150return R_TLSGD_GOT;151case R_390_TLS_LDM32:152case R_390_TLS_LDM64:153return R_TLSLD_GOT;154case R_390_TLS_LDO32:155case R_390_TLS_LDO64:156return R_DTPREL;157case R_390_TLS_LE32:158case R_390_TLS_LE64:159return R_TPREL;160case R_390_TLS_IE32:161case R_390_TLS_IE64:162return R_GOT;163case R_390_TLS_GOTIE12:164case R_390_TLS_GOTIE20:165case R_390_TLS_GOTIE32:166case R_390_TLS_GOTIE64:167return R_GOT_OFF;168case R_390_TLS_IEENT:169return R_GOT_PC;170171default:172error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +173") against symbol " + toString(s));174return R_NONE;175}176}177178void SystemZ::writeGotHeader(uint8_t *buf) const {179// _GLOBAL_OFFSET_TABLE_[0] holds the value of _DYNAMIC.180// _GLOBAL_OFFSET_TABLE_[1] and [2] are reserved.181write64be(buf, mainPart->dynamic->getVA());182}183184void SystemZ::writeGotPlt(uint8_t *buf, const Symbol &s) const {185write64be(buf, s.getPltVA() + 14);186}187188void SystemZ::writeIgotPlt(uint8_t *buf, const Symbol &s) const {189if (config->writeAddends)190write64be(buf, s.getVA());191}192193void SystemZ::writePltHeader(uint8_t *buf) const {194const uint8_t pltData[] = {1950xe3, 0x10, 0xf0, 0x38, 0x00, 0x24, // stg %r1,56(%r15)1960xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1,_GLOBAL_OFFSET_TABLE_1970xd2, 0x07, 0xf0, 0x30, 0x10, 0x08, // mvc 48(8,%r15),8(%r1)1980xe3, 0x10, 0x10, 0x10, 0x00, 0x04, // lg %r1,16(%r1)1990x07, 0xf1, // br %r12000x07, 0x00, // nopr2010x07, 0x00, // nopr2020x07, 0x00, // nopr203};204memcpy(buf, pltData, sizeof(pltData));205uint64_t got = in.got->getVA();206uint64_t plt = in.plt->getVA();207write32be(buf + 8, (got - plt - 6) >> 1);208}209210void SystemZ::addPltHeaderSymbols(InputSection &isec) const {211// The PLT header needs a reference to _GLOBAL_OFFSET_TABLE_, so we212// must ensure the .got section is created even if otherwise unused.213in.got->hasGotOffRel.store(true, std::memory_order_relaxed);214}215216void SystemZ::writePlt(uint8_t *buf, const Symbol &sym,217uint64_t pltEntryAddr) const {218const uint8_t inst[] = {2190xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1,<.got.plt slot>2200xe3, 0x10, 0x10, 0x00, 0x00, 0x04, // lg %r1,0(%r1)2210x07, 0xf1, // br %r12220x0d, 0x10, // basr %r1,%r02230xe3, 0x10, 0x10, 0x0c, 0x00, 0x14, // lgf %r1,12(%r1)2240xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, // jg <plt header>2250x00, 0x00, 0x00, 0x00, // <relocation offset>226};227memcpy(buf, inst, sizeof(inst));228229write32be(buf + 2, (sym.getGotPltVA() - pltEntryAddr) >> 1);230write32be(buf + 24, (in.plt->getVA() - pltEntryAddr - 22) >> 1);231write32be(buf + 28, in.relaPlt->entsize * sym.getPltIdx());232}233234int64_t SystemZ::getImplicitAddend(const uint8_t *buf, RelType type) const {235switch (type) {236case R_390_8:237return SignExtend64<8>(*buf);238case R_390_16:239case R_390_PC16:240return SignExtend64<16>(read16be(buf));241case R_390_PC16DBL:242return SignExtend64<16>(read16be(buf)) << 1;243case R_390_32:244case R_390_PC32:245return SignExtend64<32>(read32be(buf));246case R_390_PC32DBL:247return SignExtend64<32>(read32be(buf)) << 1;248case R_390_64:249case R_390_PC64:250case R_390_TLS_DTPMOD:251case R_390_TLS_DTPOFF:252case R_390_TLS_TPOFF:253case R_390_GLOB_DAT:254case R_390_RELATIVE:255case R_390_IRELATIVE:256return read64be(buf);257case R_390_COPY:258case R_390_JMP_SLOT:259case R_390_NONE:260// These relocations are defined as not having an implicit addend.261return 0;262default:263internalLinkerError(getErrorLocation(buf),264"cannot read addend for relocation " + toString(type));265return 0;266}267}268269RelType SystemZ::getDynRel(RelType type) const {270if (type == R_390_64 || type == R_390_PC64)271return type;272return R_390_NONE;273}274275RelExpr SystemZ::adjustTlsExpr(RelType type, RelExpr expr) const {276if (expr == R_RELAX_TLS_GD_TO_IE)277return R_RELAX_TLS_GD_TO_IE_GOT_OFF;278return expr;279}280281int SystemZ::getTlsGdRelaxSkip(RelType type) const {282// A __tls_get_offset call instruction is marked with 2 relocations:283//284// R_390_TLS_GDCALL / R_390_TLS_LDCALL: marker relocation285// R_390_PLT32DBL: __tls_get_offset286//287// After the relaxation we no longer call __tls_get_offset and should skip288// both relocations to not create a false dependence on __tls_get_offset289// being defined.290//291// Note that this mechanism only works correctly if the R_390_TLS_[GL]DCALL292// is seen immediately *before* the R_390_PLT32DBL. Unfortunately, current293// compilers on the platform will typically generate the inverse sequence.294// To fix this, we sort relocations by offset in RelocationScanner::scan;295// this ensures the correct sequence as the R_390_TLS_[GL]DCALL applies to296// the first byte of the brasl instruction, while the R_390_PLT32DBL applies297// to its third byte (the relative displacement).298299if (type == R_390_TLS_GDCALL || type == R_390_TLS_LDCALL)300return 2;301return 1;302}303304void SystemZ::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,305uint64_t val) const {306// The general-dynamic code sequence for a global `x`:307//308// Instruction Relocation Symbol309// ear %rX,%a0310// sllg %rX,%rX,32311// ear %rX,%a1312// larl %r12,_GLOBAL_OFFSET_TABLE_ R_390_GOTPCDBL _GLOBAL_OFFSET_TABLE_313// lgrl %r2,.LC0 R_390_PC32DBL .LC0314// brasl %r14,__tls_get_offset@plt R_390_TLS_GDCALL x315// :tls_gdcall:x R_390_PLT32DBL __tls_get_offset316// la %r2,0(%r2,%rX)317//318// .LC0:319// .quad x@TLSGD R_390_TLS_GD64 x320//321// Relaxing to initial-exec entails:322// 1) Replacing the call by a load from the GOT.323// 2) Replacing the relocation on the constant LC0 by R_390_TLS_GOTIE64.324325switch (rel.type) {326case R_390_TLS_GDCALL:327// brasl %r14,__tls_get_offset@plt -> lg %r2,0(%r2,%r12)328write16be(loc, 0xe322);329write32be(loc + 2, 0xc0000004);330break;331case R_390_TLS_GD64:332relocateNoSym(loc, R_390_TLS_GOTIE64, val);333break;334default:335llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");336}337}338339void SystemZ::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,340uint64_t val) const {341// The general-dynamic code sequence for a global `x`:342//343// Instruction Relocation Symbol344// ear %rX,%a0345// sllg %rX,%rX,32346// ear %rX,%a1347// larl %r12,_GLOBAL_OFFSET_TABLE_ R_390_GOTPCDBL _GLOBAL_OFFSET_TABLE_348// lgrl %r2,.LC0 R_390_PC32DBL .LC0349// brasl %r14,__tls_get_offset@plt R_390_TLS_GDCALL x350// :tls_gdcall:x R_390_PLT32DBL __tls_get_offset351// la %r2,0(%r2,%rX)352//353// .LC0:354// .quad x@tlsgd R_390_TLS_GD64 x355//356// Relaxing to local-exec entails:357// 1) Replacing the call by a nop.358// 2) Replacing the relocation on the constant LC0 by R_390_TLS_LE64.359360switch (rel.type) {361case R_390_TLS_GDCALL:362// brasl %r14,__tls_get_offset@plt -> brcl 0,.363write16be(loc, 0xc004);364write32be(loc + 2, 0x00000000);365break;366case R_390_TLS_GD64:367relocateNoSym(loc, R_390_TLS_LE64, val);368break;369default:370llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");371}372}373374void SystemZ::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,375uint64_t val) const {376// The local-dynamic code sequence for a global `x`:377//378// Instruction Relocation Symbol379// ear %rX,%a0380// sllg %rX,%rX,32381// ear %rX,%a1382// larl %r12,_GLOBAL_OFFSET_TABLE_ R_390_GOTPCDBL _GLOBAL_OFFSET_TABLE_383// lgrl %r2,.LC0 R_390_PC32DBL .LC0384// brasl %r14,__tls_get_offset@plt R_390_TLS_LDCALL <sym>385// :tls_ldcall:<sym> R_390_PLT32DBL __tls_get_offset386// la %r2,0(%r2,%rX)387// lgrl %rY,.LC1 R_390_PC32DBL .LC1388// la %r2,0(%r2,%rY)389//390// .LC0:391// .quad <sym>@tlsldm R_390_TLS_LDM64 <sym>392// .LC1:393// .quad x@dtpoff R_390_TLS_LDO64 x394//395// Relaxing to local-exec entails:396// 1) Replacing the call by a nop.397// 2) Replacing the constant LC0 by 0 (i.e. ignoring the relocation).398// 3) Replacing the relocation on the constant LC1 by R_390_TLS_LE64.399400switch (rel.type) {401case R_390_TLS_LDCALL:402// brasl %r14,__tls_get_offset@plt -> brcl 0,.403write16be(loc, 0xc004);404write32be(loc + 2, 0x00000000);405break;406case R_390_TLS_LDM64:407break;408case R_390_TLS_LDO64:409relocateNoSym(loc, R_390_TLS_LE64, val);410break;411default:412llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");413}414}415416RelExpr SystemZ::adjustGotPcExpr(RelType type, int64_t addend,417const uint8_t *loc) const {418// Only R_390_GOTENT with addend 2 can be relaxed.419if (!config->relax || addend != 2 || type != R_390_GOTENT)420return R_GOT_PC;421const uint16_t op = read16be(loc - 2);422423// lgrl rx,sym@GOTENT -> larl rx, sym424// This relaxation is legal if "sym" binds locally (which was already425// verified by our caller) and is in-range and properly aligned for a426// LARL instruction. We cannot verify the latter constraint here, so427// we assume it is true and revert the decision later on in relaxOnce428// if necessary.429if ((op & 0xff0f) == 0xc408)430return R_RELAX_GOT_PC;431432return R_GOT_PC;433}434435bool SystemZ::relaxOnce(int pass) const {436// If we decided in adjustGotPcExpr to relax a R_390_GOTENT,437// we need to validate the target symbol is in-range and aligned.438SmallVector<InputSection *, 0> storage;439bool changed = false;440for (OutputSection *osec : outputSections) {441if (!(osec->flags & SHF_EXECINSTR))442continue;443for (InputSection *sec : getInputSections(*osec, storage)) {444for (Relocation &rel : sec->relocs()) {445if (rel.expr != R_RELAX_GOT_PC)446continue;447448uint64_t v = sec->getRelocTargetVA(449sec->file, rel.type, rel.addend,450sec->getOutputSection()->addr + rel.offset, *rel.sym, rel.expr);451if (isInt<33>(v) && !(v & 1))452continue;453if (rel.sym->auxIdx == 0) {454rel.sym->allocateAux();455addGotEntry(*rel.sym);456changed = true;457}458rel.expr = R_GOT_PC;459}460}461}462return changed;463}464465void SystemZ::relaxGot(uint8_t *loc, const Relocation &rel,466uint64_t val) const {467assert(isInt<33>(val) &&468"R_390_GOTENT should not have been relaxed if it overflows");469assert(!(val & 1) &&470"R_390_GOTENT should not have been relaxed if it is misaligned");471const uint16_t op = read16be(loc - 2);472473// lgrl rx,sym@GOTENT -> larl rx, sym474if ((op & 0xff0f) == 0xc408) {475write16be(loc - 2, 0xc000 | (op & 0x00f0));476write32be(loc, val >> 1);477}478}479480void SystemZ::relocate(uint8_t *loc, const Relocation &rel,481uint64_t val) const {482switch (rel.expr) {483case R_RELAX_GOT_PC:484return relaxGot(loc, rel, val);485case R_RELAX_TLS_GD_TO_IE_GOT_OFF:486return relaxTlsGdToIe(loc, rel, val);487case R_RELAX_TLS_GD_TO_LE:488return relaxTlsGdToLe(loc, rel, val);489case R_RELAX_TLS_LD_TO_LE:490return relaxTlsLdToLe(loc, rel, val);491default:492break;493}494switch (rel.type) {495case R_390_8:496checkIntUInt(loc, val, 8, rel);497*loc = val;498break;499case R_390_12:500case R_390_GOT12:501case R_390_GOTPLT12:502case R_390_TLS_GOTIE12:503checkUInt(loc, val, 12, rel);504write16be(loc, (read16be(loc) & 0xF000) | val);505break;506case R_390_PC12DBL:507case R_390_PLT12DBL:508checkInt(loc, val, 13, rel);509checkAlignment(loc, val, 2, rel);510write16be(loc, (read16be(loc) & 0xF000) | ((val >> 1) & 0x0FFF));511break;512case R_390_16:513case R_390_GOT16:514case R_390_GOTPLT16:515case R_390_GOTOFF16:516case R_390_PLTOFF16:517checkIntUInt(loc, val, 16, rel);518write16be(loc, val);519break;520case R_390_PC16:521checkInt(loc, val, 16, rel);522write16be(loc, val);523break;524case R_390_PC16DBL:525case R_390_PLT16DBL:526checkInt(loc, val, 17, rel);527checkAlignment(loc, val, 2, rel);528write16be(loc, val >> 1);529break;530case R_390_20:531case R_390_GOT20:532case R_390_GOTPLT20:533case R_390_TLS_GOTIE20:534checkInt(loc, val, 20, rel);535write32be(loc, (read32be(loc) & 0xF00000FF) | ((val & 0xFFF) << 16) |536((val & 0xFF000) >> 4));537break;538case R_390_PC24DBL:539case R_390_PLT24DBL:540checkInt(loc, val, 25, rel);541checkAlignment(loc, val, 2, rel);542loc[0] = val >> 17;543loc[1] = val >> 9;544loc[2] = val >> 1;545break;546case R_390_32:547case R_390_GOT32:548case R_390_GOTPLT32:549case R_390_GOTOFF:550case R_390_PLTOFF32:551case R_390_TLS_IE32:552case R_390_TLS_GOTIE32:553case R_390_TLS_GD32:554case R_390_TLS_LDM32:555case R_390_TLS_LDO32:556case R_390_TLS_LE32:557checkIntUInt(loc, val, 32, rel);558write32be(loc, val);559break;560case R_390_PC32:561case R_390_PLT32:562checkInt(loc, val, 32, rel);563write32be(loc, val);564break;565case R_390_PC32DBL:566case R_390_PLT32DBL:567case R_390_GOTPCDBL:568case R_390_GOTENT:569case R_390_GOTPLTENT:570case R_390_TLS_IEENT:571checkInt(loc, val, 33, rel);572checkAlignment(loc, val, 2, rel);573write32be(loc, val >> 1);574break;575case R_390_64:576case R_390_PC64:577case R_390_PLT64:578case R_390_GOT64:579case R_390_GOTPLT64:580case R_390_GOTOFF64:581case R_390_PLTOFF64:582case R_390_GOTPC:583case R_390_TLS_IE64:584case R_390_TLS_GOTIE64:585case R_390_TLS_GD64:586case R_390_TLS_LDM64:587case R_390_TLS_LDO64:588case R_390_TLS_LE64:589case R_390_TLS_DTPMOD:590case R_390_TLS_DTPOFF:591case R_390_TLS_TPOFF:592write64be(loc, val);593break;594case R_390_TLS_LOAD:595case R_390_TLS_GDCALL:596case R_390_TLS_LDCALL:597break;598default:599llvm_unreachable("unknown relocation");600}601}602603TargetInfo *elf::getSystemZTargetInfo() {604static SystemZ t;605return &t;606}607608609