diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h index 49d2809cb6524..f54d8cd19c86b 100644 --- a/llvm/include/llvm/BinaryFormat/GOFF.h +++ b/llvm/include/llvm/BinaryFormat/GOFF.h @@ -157,6 +157,29 @@ enum ESDAlignment : uint8_t { ESD_ALIGN_4Kpage = 12, }; +enum RLDReferenceType : uint8_t { + RLD_RT_RAddress = 0, + RLD_RT_ROffset = 1, + RLD_RT_RLength = 2, + RLD_RT_RRelativeImmediate = 6, + RLD_RT_RTypeConstant = 7, + RLD_RT_RLongDisplacement = 9, +}; + +enum RLDReferentType : uint8_t { + RLD_RO_Label = 0, + RLD_RO_Element = 1, + RLD_RO_Class = 2, + RLD_RO_Part = 3, +}; + +enum RLDAction : uint8_t { + RLD_ACT_Add = 0, + RLD_ACT_Subtract = 1, +}; + +enum RLDFetchStore : uint8_t { RLD_FS_Fetch = 0, RLD_FS_Store = 1 }; + enum ENDEntryPointRequest : uint8_t { END_EPR_None = 0, END_EPR_EsdidOffset = 1, diff --git a/llvm/include/llvm/MC/MCGOFFObjectWriter.h b/llvm/include/llvm/MC/MCGOFFObjectWriter.h index ec07637dd2847..4124c0308dbd9 100644 --- a/llvm/include/llvm/MC/MCGOFFObjectWriter.h +++ b/llvm/include/llvm/MC/MCGOFFObjectWriter.h @@ -9,11 +9,16 @@ #ifndef LLVM_MC_MCGOFFOBJECTWRITER_H #define LLVM_MC_MCGOFFOBJECTWRITER_H +#include "llvm/BinaryFormat/GOFF.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCValue.h" +#include +#include namespace llvm { class MCObjectWriter; +class MCSectionGOFF; +class MCSymbolGOFF; class raw_pwrite_stream; class MCGOFFObjectTargetWriter : public MCObjectTargetWriter { @@ -21,8 +26,19 @@ class MCGOFFObjectTargetWriter : public MCObjectTargetWriter { MCGOFFObjectTargetWriter() = default; public: + enum RLDRelocationType { + Reloc_Type_ACon = 0x1, // General address. + Reloc_Type_RelImm = 0x2, // Relative-immediate address. + Reloc_Type_QCon = 0x3, // Offset of symbol in class. + Reloc_Type_VCon = 0x4, // Address of external symbol. + Reloc_Type_RCon = 0x5, // PSECT of symbol. + }; + ~MCGOFFObjectTargetWriter() override = default; + virtual unsigned getRelocType(const MCValue &Target, + const MCFixup &Fixup) const = 0; + Triple::ObjectFormatType getFormat() const override { return Triple::GOFF; } static bool classof(const MCObjectTargetWriter *W) { @@ -30,6 +46,33 @@ class MCGOFFObjectTargetWriter : public MCObjectTargetWriter { } }; +// A GOFFRelocationEntry describes a single relocation. +// For the naming, see +// https://www.ibm.com/docs/en/zos/3.1.0?topic=record-relocation-directory-data-item. +struct GOFFRelocationEntry { + const MCSymbolGOFF *Rptr; // The R pointer. + const MCSectionGOFF *Pptr; // The P pointer. + uint32_t REsdId = 0; // The R pointer id. + uint32_t PEsdId = 0; // The P pointer id. + uint64_t POffset; // The offset within the element described by the P pointer. + uint32_t TargetLength; // The byte length of the target field. + + // Details of the relocation. + GOFF::RLDReferenceType ReferenceType : 4; + GOFF::RLDReferentType ReferentType : 2; + GOFF::RLDAction Action : 1; + GOFF::RLDFetchStore FetchStore : 1; + + GOFFRelocationEntry(const MCSectionGOFF *Pptr, const MCSymbolGOFF *Rptr, + GOFF::RLDReferenceType ReferenceType, + GOFF::RLDReferentType ReferentType, + GOFF::RLDAction Action, GOFF::RLDFetchStore FetchStore, + uint64_t POffset, uint32_t TargetLength) + : Rptr(Rptr), Pptr(Pptr), POffset(POffset), TargetLength(TargetLength), + ReferenceType(ReferenceType), ReferentType(ReferentType), + Action(Action), FetchStore(FetchStore) {} +}; + class GOFFObjectWriter : public MCObjectWriter { // The target specific GOFF writer instance. std::unique_ptr TargetObjectWriter; @@ -37,6 +80,9 @@ class GOFFObjectWriter : public MCObjectWriter { // The stream used to write the GOFF records. raw_pwrite_stream &OS; + // Saved relocation data. + std::vector Relocations; + public: GOFFObjectWriter(std::unique_ptr MOTW, raw_pwrite_stream &OS); @@ -44,7 +90,7 @@ class GOFFObjectWriter : public MCObjectWriter { // Implementation of the MCObjectWriter interface. void recordRelocation(const MCFragment &F, const MCFixup &Fixup, - MCValue Target, uint64_t &FixedValue) override {} + MCValue Target, uint64_t &FixedValue) override; uint64_t writeObject() override; }; diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp index 07aecf13bce37..3be862e713200 100644 --- a/llvm/lib/MC/GOFFObjectWriter.cpp +++ b/llvm/lib/MC/GOFFObjectWriter.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/BinaryFormat/GOFF.h" +#include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCGOFFAttributes.h" #include "llvm/MC/MCGOFFObjectWriter.h" @@ -284,9 +285,13 @@ class GOFFWriter { GOFFOstream OS; MCAssembler &Asm; + /// Saved relocation data collected in recordRelocations(). + std::vector &Relocations; + void writeHeader(); void writeSymbol(const GOFFSymbol &Symbol); void writeText(const MCSectionGOFF *MC); + void writeRelocations(); void writeEnd(); void defineSectionSymbols(const MCSectionGOFF &Section); @@ -295,13 +300,15 @@ class GOFFWriter { void defineSymbols(); public: - GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm); + GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm, + std::vector &Relocations); uint64_t writeObject(); }; } // namespace -GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm) - : OS(OS), Asm(Asm) {} +GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm, + std::vector &Relocations) + : OS(OS), Asm(Asm), Relocations(Relocations) {} void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) { if (Section.isSD()) { @@ -502,6 +509,117 @@ void GOFFWriter::writeText(const MCSectionGOFF *Section) { Asm.writeSectionData(S, Section); } +namespace { +// RelocDataItemBuffer provides a static buffer for relocation data items. +class RelocDataItemBuffer { + char Buffer[GOFF::MaxDataLength]; + char *Ptr; + +public: + RelocDataItemBuffer() : Ptr(Buffer) {} + const char *data() { return Buffer; } + size_t size() { return Ptr - Buffer; } + void reset() { Ptr = Buffer; } + bool fits(size_t S) { return size() + S < GOFF::MaxDataLength; } + template void writebe(T Val) { + assert(fits(sizeof(T)) && "Out-of-bounds write"); + support::endian::write(Ptr, Val); + Ptr += sizeof(T); + } +}; +} // namespace + +void GOFFWriter::writeRelocations() { + // Set the IDs in the relocation entries. + for (auto &RelocEntry : Relocations) { + auto GetRptr = [](const MCSymbolGOFF *Sym) -> uint32_t { + if (Sym->isTemporary()) + return static_cast(Sym->getSection()) + .getBeginSymbol() + ->getIndex(); + return Sym->getIndex(); + }; + + RelocEntry.PEsdId = RelocEntry.Pptr->getOrdinal(); + RelocEntry.REsdId = GetRptr(RelocEntry.Rptr); + } + + // Sort relocation data items by the P pointer to save space. + std::sort( + Relocations.begin(), Relocations.end(), + [](const GOFFRelocationEntry &Left, const GOFFRelocationEntry &Right) { + return std::tie(Left.PEsdId, Left.REsdId, Left.POffset) < + std::tie(Right.PEsdId, Right.REsdId, Right.POffset); + }); + + // Construct the compressed relocation data items, and write them out. + RelocDataItemBuffer Buffer; + for (auto I = Relocations.begin(), E = Relocations.end(); I != E;) { + Buffer.reset(); + + uint32_t PrevResdId = -1; + uint32_t PrevPesdId = -1; + uint64_t PrevPOffset = -1; + for (; I != E; ++I) { + const GOFFRelocationEntry &Rel = *I; + + bool SameREsdId = (Rel.REsdId == PrevResdId); + bool SamePEsdId = (Rel.PEsdId == PrevPesdId); + bool SamePOffset = (Rel.POffset == PrevPOffset); + bool EightByteOffset = ((Rel.POffset >> 32) & 0xffffffff); + + // Calculate size of relocation data item, and check if it still fits into + // the record. + size_t ItemSize = 8; // Smallest size of a relocation data item. + if (!SameREsdId) + ItemSize += 4; + if (!SamePEsdId) + ItemSize += 4; + if (!SamePOffset) + ItemSize += (EightByteOffset ? 8 : 4); + if (!Buffer.fits(ItemSize)) + break; + + GOFF::Flags RelocFlags[6]; + RelocFlags[0].set(0, 1, SameREsdId); + RelocFlags[0].set(1, 1, SamePEsdId); + RelocFlags[0].set(2, 1, SamePOffset); + RelocFlags[0].set(6, 1, EightByteOffset); + + RelocFlags[1].set(0, 4, Rel.ReferenceType); + RelocFlags[1].set(4, 4, Rel.ReferentType); + + RelocFlags[2].set(0, 7, Rel.Action); + RelocFlags[2].set(7, 1, Rel.FetchStore); + + RelocFlags[4].set(0, 8, Rel.TargetLength); + + for (auto F : RelocFlags) + Buffer.writebe(F); + Buffer.writebe(0); // Reserved. + if (!SameREsdId) + Buffer.writebe(Rel.REsdId); + if (!SamePEsdId) + Buffer.writebe(Rel.PEsdId); + if (!SamePOffset) { + if (EightByteOffset) + Buffer.writebe(Rel.POffset); + else + Buffer.writebe(Rel.POffset); + } + + PrevResdId = Rel.REsdId; + PrevPesdId = Rel.PEsdId; + PrevPOffset = Rel.POffset; + } + + OS.newRecord(GOFF::RT_RLD); + OS.writebe(0); // Reserved. + OS.writebe(Buffer.size()); // Length (of the relocation data). + OS.write(Buffer.data(), Buffer.size()); // Relocation Directory Data Items. + } +} + void GOFFWriter::writeEnd() { uint8_t F = GOFF::END_EPR_None; uint8_t AMODE = 0; @@ -528,6 +646,8 @@ uint64_t GOFFWriter::writeObject() { for (const MCSection &Section : Asm) writeText(static_cast(&Section)); + writeRelocations(); + writeEnd(); // Make sure all records are written. @@ -545,8 +665,117 @@ GOFFObjectWriter::GOFFObjectWriter( GOFFObjectWriter::~GOFFObjectWriter() = default; +void GOFFObjectWriter::recordRelocation(const MCFragment &F, + const MCFixup &Fixup, MCValue Target, + uint64_t &FixedValue) { + const MCFixupKindInfo &FKI = + Asm->getBackend().getFixupKindInfo(Fixup.getKind()); + const uint32_t Length = FKI.TargetSize / 8; + assert(FKI.TargetSize % 8 == 0 && "Target Size not multiple of 8"); + const uint64_t FixupOffset = Asm->getFragmentOffset(F) + Fixup.getOffset(); + + unsigned RelocType = TargetObjectWriter->getRelocType(Target, Fixup); + + const MCSectionGOFF *PSection = static_cast(F.getParent()); + const auto &A = *static_cast(Target.getAddSym()); + const MCSymbolGOFF *B = static_cast(Target.getSubSym()); + if (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RelImm) { + if (A.isUndefined()) { + Asm->reportError( + Fixup.getLoc(), + Twine("symbol ") + .concat(A.getName()) + .concat(" must be defined for a relative immediate relocation")); + return; + } + if (&A.getSection() != PSection) { + Asm->reportError(Fixup.getLoc(), + Twine("relative immediate relocation section mismatch: ") + .concat(A.getSection().getName()) + .concat(" of symbol ") + .concat(A.getName()) + .concat(" <-> ") + .concat(PSection->getName())); + return; + } + if (B) { + Asm->reportError( + Fixup.getLoc(), + Twine("subtractive symbol ") + .concat(B->getName()) + .concat(" not supported for a relative immediate relocation")); + return; + } + FixedValue = Asm->getSymbolOffset(A) - FixupOffset + Target.getConstant(); + return; + } + FixedValue = Target.getConstant(); + + // The symbol only has a section-relative offset if it is a temporary symbol. + FixedValue += A.isTemporary() ? Asm->getSymbolOffset(A) : 0; + A.setUsedInReloc(); + if (B) { + FixedValue -= B->isTemporary() ? Asm->getSymbolOffset(*B) : 0; + B->setUsedInReloc(); + } + + // UseQCon causes class offsets versus absolute addresses to be used. This + // is analogous to using QCONs in older OBJ object file format. + bool UseQCon = RelocType == MCGOFFObjectTargetWriter::Reloc_Type_QCon; + + GOFF::RLDFetchStore FetchStore = + (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RCon || + RelocType == MCGOFFObjectTargetWriter::Reloc_Type_VCon) + ? GOFF::RLDFetchStore::RLD_FS_Store + : GOFF::RLDFetchStore::RLD_FS_Fetch; + assert(FetchStore == GOFF::RLDFetchStore::RLD_FS_Fetch || + B == nullptr && "No dependent relocations expected"); + + enum GOFF::RLDReferenceType ReferenceType = GOFF::RLD_RT_RAddress; + enum GOFF::RLDReferentType ReferentType = GOFF::RLD_RO_Label; + if (UseQCon) { + ReferenceType = GOFF::RLD_RT_ROffset; + ReferentType = GOFF::RLD_RO_Class; + } + if (RelocType == MCGOFFObjectTargetWriter::Reloc_Type_RCon) + ReferenceType = GOFF::RLD_RT_RTypeConstant; + + auto DumpReloc = [&PSection, &ReferenceType, &FixupOffset, + &FixedValue](const char *N, const MCSymbolGOFF *Sym) { + const char *Con; + switch (ReferenceType) { + case GOFF::RLDReferenceType::RLD_RT_RAddress: + Con = "ACon"; + break; + case GOFF::RLDReferenceType::RLD_RT_ROffset: + Con = "QCon"; + break; + case GOFF::RLDReferenceType::RLD_RT_RTypeConstant: + Con = "VCon"; + break; + default: + Con = "(unknown)"; + } + dbgs() << "Reloc " << N << ": " << Con << " Rptr: " << Sym->getName() + << " Pptr: " << PSection->getName() << " Offset: " << FixupOffset + << " Fixed Imm: " << FixedValue << "\n"; + }; + (void)DumpReloc; + + // Save relocation data for later writing. + LLVM_DEBUG(DumpReloc("A", &A)); + Relocations.emplace_back(PSection, &A, ReferenceType, ReferentType, + GOFF::RLD_ACT_Add, FetchStore, FixupOffset, Length); + if (B) { + LLVM_DEBUG(DumpReloc("B", B)); + Relocations.emplace_back( + PSection, B, ReferenceType, ReferentType, GOFF::RLD_ACT_Subtract, + GOFF::RLDFetchStore::RLD_FS_Fetch, FixupOffset, Length); + } +} + uint64_t GOFFObjectWriter::writeObject() { - uint64_t Size = GOFFWriter(OS, *Asm).writeObject(); + uint64_t Size = GOFFWriter(OS, *Asm, Relocations).writeObject(); return Size; } diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp index 205066814fbd0..cfbdd5d4780ab 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/SystemZMCTargetDesc.h" +#include "SystemZMCAsmInfo.h" #include "llvm/MC/MCGOFFObjectWriter.h" #include @@ -16,12 +17,34 @@ namespace { class SystemZGOFFObjectWriter : public MCGOFFObjectTargetWriter { public: SystemZGOFFObjectWriter(); + + unsigned getRelocType(const MCValue &Target, + const MCFixup &Fixup) const override; }; } // end anonymous namespace SystemZGOFFObjectWriter::SystemZGOFFObjectWriter() : MCGOFFObjectTargetWriter() {} +unsigned SystemZGOFFObjectWriter::getRelocType(const MCValue &Target, + const MCFixup &Fixup) const { + switch (Target.getSpecifier()) { + case SystemZ::S_PLT: // TODO This doen't make sense. + return Reloc_Type_RelImm; + case SystemZ::S_RCon: + return Reloc_Type_RCon; + case SystemZ::S_VCon: + return Reloc_Type_VCon; + case SystemZ::S_QCon: + return Reloc_Type_QCon; + case SystemZ::S_None: + if (Fixup.isPCRel()) + return Reloc_Type_RelImm; + return Reloc_Type_ACon; + } + llvm_unreachable("Modifier not supported"); +} + std::unique_ptr llvm::createSystemZGOFFObjectWriter() { return std::make_unique(); } diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.h index 11c2833b8ada8..668158b896856 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.h +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.h @@ -51,6 +51,7 @@ enum { // https://www.ibm.com/docs/en/hla-and-tf/1.6?topic=value-address-constants S_RCon, // Address of ADA of symbol. S_VCon, // Address of external function symbol. + S_QCon, // Class-based offset. }; } // namespace SystemZ diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll index cae371b07d0eb..f4e245addc2c3 100644 --- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll +++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll @@ -145,9 +145,22 @@ entry: ; CHECK: 000550 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00 ; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}} +; The relocation data directory. +; CHECK: 0005a0 03 21 00 00 00 5c 00 00 02 00 04 00 00 00 00 00 +; CHECK-NEXT: 0005b0 00 08 00 00 00 02 00 00 00 5a 60 00 00 00 04 00 +; CHECK-NEXT: 0005c0 00 00 00 00 00 09 00 00 00 00 08 00 00 00 00 00 +; CHECK-NEXT: 0005d0 00 08 00 00 00 04 00 00 00 00 60 00 02 00 08 00 +; CHECK-NEXT: 0005e0 00 00 00 00 00 09 20 70 01 00 08 00 00 00 00 00 +; Continuation of the relocation data directory. +; CHECK-NEXT: 0005f0 03 22 00 00 0b 00 00 00 06 c0 00 01 00 08 00 00 +; CHECK-NEXT: 000600 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + ; End record. -; CHECK: 0005a0 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0005c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0005d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0005e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK: 000640 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000680 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 diff --git a/llvm/test/CodeGen/SystemZ/zos-section-2.ll b/llvm/test/CodeGen/SystemZ/zos-section-2.ll index f59bace65ca73..d3eb6d9505340 100644 --- a/llvm/test/CodeGen/SystemZ/zos-section-2.ll +++ b/llvm/test/CodeGen/SystemZ/zos-section-2.ll @@ -175,9 +175,15 @@ source_filename = "test.ll" ; CHECK: 000640 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00 ; CHECK-NEXT: 000650 00 00 00 00 00 00 00 {{..}} {{.*}} +; The relocation data directory +; CHECK: 000690 03 20 00 00 00 40 00 00 02 00 04 00 00 00 00 00 +; CHECK-NEXT: 0006a0 00 0e 00 00 00 02 00 00 00 04 60 00 00 00 04 00 +; CHECK-NEXT: 0006b0 00 00 00 00 00 0f 00 00 00 00 08 00 00 00 00 00 +; CHECK-NEXT: 0006c0 00 0e 00 00 00 04 00 00 00 00 60 00 02 00 08 00 +; CHECK-NEXT: 0006d0 00 00 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 + ; End record. -; CHECK: 000690 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0006a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0006b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0006c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0006d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK: 0006e0 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0006f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000710 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00