diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h new file mode 100644 index 0000000000000..3d181d0786eb7 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h @@ -0,0 +1,37 @@ +//===------- XCOFF.h - Generic JIT link function for XCOFF ------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// jit-link functions for XCOFF. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_XCOFF_H +#define LLVM_EXECUTIONENGINE_JITLINK_XCOFF_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// Create a LinkGraph from an XCOFF relocatable object. +/// +/// Note: The graph does not take ownership of the underlying buffer, nor copy +/// its contents. The caller is responsible for ensuring that the object buffer +/// outlives the graph. +Expected> +createLinkGraphFromXCOFFObject(MemoryBufferRef ObjectBuffer, + std::shared_ptr SSP); + +/// Link the given graph. +void link_XCOFF(std::unique_ptr G, + std::unique_ptr Ctx); + +} // namespace jitlink +} // namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_XCOFF_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h new file mode 100644 index 0000000000000..ec5c8a37bda27 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h @@ -0,0 +1,37 @@ +//===------ XCOFF_ppc64.h - JIT link functions for XCOFF/ppc64 ------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// jit-link functions for XCOFF/ppc64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_XCOFF_PPC64_H +#define LLVM_EXECUTIONENGINE_JITLINK_XCOFF_PPC64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm::jitlink { + +/// Create a LinkGraph from an XCOFF/ppc64 relocatable object. +/// +/// Note: The graph does not take ownership of the underlying buffer, nor copy +/// its contents. The caller is responsible for ensuring that the object buffer +/// outlives the graph. +/// +Expected> createLinkGraphFromXCOFFObject_ppc64( + MemoryBufferRef ObjectBuffer, std::shared_ptr SSP); + +/// jit-link the given object buffer, which must be a XCOFF ppc64 object file. +/// +void link_XCOFF_ppc64(std::unique_ptr G, + std::unique_ptr Ctx); + +} // end namespace llvm::jitlink + +#endif // LLVM_EXECUTIONENGINE_JITLINK_XCOFF_PPC64_H diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt index 65dd0c7468ae1..22e4513e1374c 100644 --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -35,6 +35,11 @@ add_llvm_component_library(LLVMJITLink COFFLinkGraphBuilder.cpp COFF_x86_64.cpp + # XCOFF + XCOFF.cpp + XCOFF_ppc64.cpp + XCOFFLinkGraphBuilder.cpp + # Architectures: aarch32.cpp aarch64.cpp diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp index e8ce9b2b9527d..15a8fcf312ade 100644 --- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp @@ -13,6 +13,7 @@ #include "llvm/ExecutionEngine/JITLink/COFF.h" #include "llvm/ExecutionEngine/JITLink/ELF.h" #include "llvm/ExecutionEngine/JITLink/MachO.h" +#include "llvm/ExecutionEngine/JITLink/XCOFF.h" #include "llvm/ExecutionEngine/JITLink/aarch64.h" #include "llvm/ExecutionEngine/JITLink/i386.h" #include "llvm/ExecutionEngine/JITLink/loongarch.h" @@ -501,6 +502,8 @@ createLinkGraphFromObject(MemoryBufferRef ObjectBuffer, return createLinkGraphFromELFObject(ObjectBuffer, std::move(SSP)); case file_magic::coff_object: return createLinkGraphFromCOFFObject(ObjectBuffer, std::move(SSP)); + case file_magic::xcoff_object_64: + return createLinkGraphFromXCOFFObject(ObjectBuffer, std::move(SSP)); default: return make_error("Unsupported file format"); }; @@ -532,6 +535,8 @@ void link(std::unique_ptr G, std::unique_ptr Ctx) { return link_ELF(std::move(G), std::move(Ctx)); case Triple::COFF: return link_COFF(std::move(G), std::move(Ctx)); + case Triple::XCOFF: + return link_XCOFF(std::move(G), std::move(Ctx)); default: Ctx->notifyFailed(make_error("Unsupported object format")); }; diff --git a/llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp b/llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp new file mode 100644 index 0000000000000..cb026538632a9 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp @@ -0,0 +1,43 @@ +//===-------------- XCOFF.cpp - JIT linker function for XCOFF -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// XCOFF jit-link function. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/XCOFF.h" +#include "llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm; + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { + +Expected> +createLinkGraphFromXCOFFObject(MemoryBufferRef ObjectBuffer, + std::shared_ptr SSP) { + // Check magic + file_magic Magic = identify_magic(ObjectBuffer.getBuffer()); + if (Magic != file_magic::xcoff_object_64) + return make_error("Invalid XCOFF 64 Header"); + + // TODO: See if we need to add more checks + // + return createLinkGraphFromXCOFFObject_ppc64(ObjectBuffer, std::move(SSP)); +} + +void link_XCOFF(std::unique_ptr G, + std::unique_ptr Ctx) { + link_XCOFF_ppc64(std::move(G), std::move(Ctx)); +} + +} // namespace jitlink +} // namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp new file mode 100644 index 0000000000000..b871ecfb4f0d8 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp @@ -0,0 +1,415 @@ +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic XCOFF LinkGraph building code. +// +//===----------------------------------------------------------------------===// + +#include "XCOFFLinkGraphBuilder.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/ppc64.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { + +XCOFFLinkGraphBuilder::XCOFFLinkGraphBuilder( + const object::XCOFFObjectFile &Obj, + std::shared_ptr SSP, Triple TT, + SubtargetFeatures Features, + LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) + : Obj(Obj), + G(std::make_unique( + std::string(Obj.getFileName()), std::move(SSP), std::move(TT), + std::move(Features), std::move(GetEdgeKindName))) {} + +static llvm::StringRef getStorageClassString(XCOFF::StorageClass SC) { + switch (SC) { + case XCOFF::StorageClass::C_FILE: + return "C_FILE (File name)"; + case XCOFF::StorageClass::C_BINCL: + return "C_BINCL (Beginning of include file)"; + case XCOFF::StorageClass::C_EINCL: + return "C_EINCL (Ending of include file)"; + case XCOFF::StorageClass::C_GSYM: + return "C_GSYM (Global variable)"; + case XCOFF::StorageClass::C_STSYM: + return "C_STSYM (Statically allocated symbol)"; + case XCOFF::StorageClass::C_BCOMM: + return "C_BCOMM (Beginning of common block)"; + case XCOFF::StorageClass::C_ECOMM: + return "C_ECOMM (End of common block)"; + case XCOFF::StorageClass::C_ENTRY: + return "C_ENTRY (Alternate entry)"; + case XCOFF::StorageClass::C_BSTAT: + return "C_BSTAT (Beginning of static block)"; + case XCOFF::StorageClass::C_ESTAT: + return "C_ESTAT (End of static block)"; + case XCOFF::StorageClass::C_GTLS: + return "C_GTLS (Global thread-local variable)"; + case XCOFF::StorageClass::C_STTLS: + return "C_STTLS (Static thread-local variable)"; + case XCOFF::StorageClass::C_DWARF: + return "C_DWARF (DWARF section symbol)"; + case XCOFF::StorageClass::C_LSYM: + return "C_LSYM (Automatic variable allocated on stack)"; + case XCOFF::StorageClass::C_PSYM: + return "C_PSYM (Argument to subroutine allocated on stack)"; + case XCOFF::StorageClass::C_RSYM: + return "C_RSYM (Register variable)"; + case XCOFF::StorageClass::C_RPSYM: + return "C_RPSYM (Argument to function stored in register)"; + case XCOFF::StorageClass::C_ECOML: + return "C_ECOML (Local member of common block)"; + case XCOFF::StorageClass::C_FUN: + return "C_FUN (Function or procedure)"; + case XCOFF::StorageClass::C_EXT: + return "C_EXT (External symbol)"; + case XCOFF::StorageClass::C_WEAKEXT: + return "C_WEAKEXT (Weak external symbol)"; + case XCOFF::StorageClass::C_NULL: + return "C_NULL"; + case XCOFF::StorageClass::C_STAT: + return "C_STAT (Static)"; + case XCOFF::StorageClass::C_BLOCK: + return "C_BLOCK (\".bb\" or \".eb\")"; + case XCOFF::StorageClass::C_FCN: + return "C_FCN (\".bf\" or \".ef\")"; + case XCOFF::StorageClass::C_HIDEXT: + return "C_HIDEXT (Un-named external symbol)"; + case XCOFF::StorageClass::C_INFO: + return "C_INFO (Comment string in .info section)"; + case XCOFF::StorageClass::C_DECL: + return "C_DECL (Declaration of object)"; + case XCOFF::StorageClass::C_AUTO: + return "C_AUTO (Automatic variable)"; + case XCOFF::StorageClass::C_REG: + return "C_REG (Register variable)"; + case XCOFF::StorageClass::C_EXTDEF: + return "C_EXTDEF (External definition)"; + case XCOFF::StorageClass::C_LABEL: + return "C_LABEL (Label)"; + case XCOFF::StorageClass::C_ULABEL: + return "C_ULABEL (Undefined label)"; + case XCOFF::StorageClass::C_MOS: + return "C_MOS (Member of structure)"; + case XCOFF::StorageClass::C_ARG: + return "C_ARG (Function argument)"; + case XCOFF::StorageClass::C_STRTAG: + return "C_STRTAG (Structure tag)"; + case XCOFF::StorageClass::C_MOU: + return "C_MOU (Member of union)"; + case XCOFF::StorageClass::C_UNTAG: + return "C_UNTAG (Union tag)"; + case XCOFF::StorageClass::C_TPDEF: + return "C_TPDEF (Type definition)"; + case XCOFF::StorageClass::C_USTATIC: + return "C_USTATIC (Undefined static)"; + case XCOFF::StorageClass::C_ENTAG: + return "C_ENTAG (Enumeration tag)"; + case XCOFF::StorageClass::C_MOE: + return "C_MOE (Member of enumeration)"; + case XCOFF::StorageClass::C_REGPARM: + return "C_REGPARM (Register parameter)"; + case XCOFF::StorageClass::C_FIELD: + return "C_FIELD (Bit field)"; + case XCOFF::StorageClass::C_EOS: + return "C_EOS (End of structure)"; + case XCOFF::StorageClass::C_LINE: + return "C_LINE"; + case XCOFF::StorageClass::C_ALIAS: + return "C_ALIAS (Duplicate tag)"; + case XCOFF::StorageClass::C_HIDDEN: + return "C_HIDDEN (Special storage class for external)"; + case XCOFF::StorageClass::C_EFCN: + return "C_EFCN (Physical end of function)"; + case XCOFF::StorageClass::C_TCSYM: + return "C_TCSYM (Reserved)"; + } +} + +Error XCOFFLinkGraphBuilder::processSections() { + LLVM_DEBUG(dbgs() << " Creating graph sections...\n"); + + UndefSection = &G->createSection("*UND*", orc::MemProt::None); + + for (object::SectionRef Section : Obj.sections()) { + auto SectionName = Section.getName(); + if (!SectionName) + return SectionName.takeError(); + + LLVM_DEBUG({ + dbgs() << " section = " << *SectionName + << ", idx = " << Section.getIndex() + << ", size = " << format_hex_no_prefix(Section.getSize(), 8) + << ", vma = " << format_hex(Section.getAddress(), 16) << "\n"; + }); + + // We can skip debug (including dawrf) and pad sections + if (Section.isDebugSection() || *SectionName == "pad") + continue; + LLVM_DEBUG(dbgs() << " creating graph section\n"); + + orc::MemProt Prot = orc::MemProt::Read; + if (Section.isText()) + Prot |= orc::MemProt::Exec; + if (Section.isData() || Section.isBSS()) + Prot |= orc::MemProt::Write; + + jitlink::Section *GraphSec = &G->createSection(*SectionName, Prot); + // TODO: Check for no_alloc for certain sections + + assert(!SectionTable.contains(Section.getIndex()) && + "Section with same index already exists"); + SectionTable[Section.getIndex()] = {GraphSec, Section}; + } + + return Error::success(); +} + +static std::optional +getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile &Obj, + const object::SymbolRef &Sym) { + const object::XCOFFSymbolRef SymRef = + Obj.toSymbolRef(Sym.getRawDataRefImpl()); + if (!SymRef.isCsectSymbol()) + return std::nullopt; + + Expected CsectAuxEntOrErr = + SymRef.getXCOFFCsectAuxRef(); + if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel()) + return std::nullopt; + uint32_t Idx = + static_cast(CsectAuxEntOrErr.get().getSectionOrLength()); + object::DataRefImpl DRI; + DRI.p = Obj.getSymbolByIndex(Idx); + return object::XCOFFSymbolRef(DRI, &Obj); +} + +static void printSymbolEntry(raw_ostream &OS, + const object::XCOFFObjectFile &Obj, + const object::XCOFFSymbolRef &Sym) { + OS << " " << format_hex(cantFail(Sym.getAddress()), 16); + OS << " " << left_justify(cantFail(Sym.getName()), 10); + if (Sym.isCsectSymbol()) { + auto CsectAuxEntry = cantFail(Sym.getXCOFFCsectAuxRef()); + if (!CsectAuxEntry.isLabel()) { + std::string MCStr = + "[" + + XCOFF::getMappingClassString(CsectAuxEntry.getStorageMappingClass()) + .str() + + "]"; + OS << left_justify(MCStr, 3); + } + } + OS << " " << format_hex(Sym.getSize(), 8); + OS << " " << Sym.getSectionNumber(); + OS << " " << getStorageClassString(Sym.getStorageClass()); + OS << " (idx: " << Obj.getSymbolIndex(Sym.getRawDataRefImpl().p) << ")"; + if (Sym.isCsectSymbol()) { + if (auto ParentSym = getXCOFFSymbolContainingSymbolRef(Obj, Sym)) { + OS << " (csect idx: " + << Obj.getSymbolIndex(ParentSym->getRawDataRefImpl().p) << ")"; + } + } + OS << "\n"; +} + +Error XCOFFLinkGraphBuilder::processCsectsAndSymbols() { + LLVM_DEBUG(dbgs() << " Creating graph blocks and symbols...\n"); + + for (auto [K, V] : SectionTable) { + LLVM_DEBUG(dbgs() << " section entry(idx: " << K + << " section: " << V.Section->getName() << ")\n"); + } + + for (object::XCOFFSymbolRef Symbol : Obj.symbols()) { + LLVM_DEBUG({ printSymbolEntry(dbgs(), Obj, Symbol); }); + + auto Flags = Symbol.getFlags(); + if (!Flags) + return Flags.takeError(); + + bool External = *Flags & object::SymbolRef::SF_Undefined; + bool Weak = *Flags & object::SymbolRef::SF_Weak; + bool Global = *Flags & object::SymbolRef::SF_Global; + + auto SymbolIndex = Obj.getSymbolIndex(Symbol.getEntryAddress()); + auto SymbolName = Symbol.getName(); + if (!SymbolName) + return SymbolName.takeError(); + + if (External) { + LLVM_DEBUG(dbgs() << " created external symbol\n"); + SymbolIndexTable[SymbolIndex] = + &G->addExternalSymbol(*SymbolName, Symbol.getSize(), Weak); + continue; + } + + if (!Symbol.isCsectSymbol()) { + LLVM_DEBUG(dbgs() << " skipped: not a csect symbol\n"); + continue; + } + + auto ParentSym = getXCOFFSymbolContainingSymbolRef(Obj, Symbol); + object::XCOFFSymbolRef CsectSymbol = ParentSym ? *ParentSym : Symbol; + + auto CsectSymbolIndex = Obj.getSymbolIndex(CsectSymbol.getEntryAddress()); + auto ParentSectionNumber = CsectSymbol.getSectionNumber(); + + bool IsUndefinedSection = !SectionTable.contains(ParentSectionNumber); + Section *ParentSection = !IsUndefinedSection + ? SectionTable[ParentSectionNumber].Section + : UndefSection; + Block *B = nullptr; + + // TODO: Clean up the logic for handling undefined symbols + if (!CsectTable.contains(CsectSymbolIndex) && !IsUndefinedSection) { + object::SectionRef &SectionRef = + SectionTable[ParentSectionNumber].SectionData; + auto Data = SectionRef.getContents(); + if (!Data) + return Data.takeError(); + auto CsectSymbolAddr = CsectSymbol.getAddress(); + if (!CsectSymbolAddr) + return CsectSymbolAddr.takeError(); + + ArrayRef SectionBuffer{Data->data(), Data->size()}; + auto Offset = *CsectSymbolAddr - SectionRef.getAddress(); + + LLVM_DEBUG(dbgs() << " symbol entry: offset = " << Offset + << ", size = " << CsectSymbol.getSize() + << ", storage class = " + << getStorageClassString(CsectSymbol.getStorageClass()) + << "\n"); + + B = &G->createContentBlock( + *ParentSection, SectionBuffer.slice(Offset, CsectSymbol.getSize()), + orc::ExecutorAddr(*CsectSymbolAddr), CsectSymbol.getAlignment(), 0); + + CsectTable[CsectSymbolIndex] = B; + } else { + B = CsectTable[CsectSymbolIndex]; + } + + Scope S{Scope::Local}; + if (Symbol.getSymbolType() & XCOFF::SYM_V_HIDDEN || + Symbol.getSymbolType() & XCOFF::SYM_V_INTERNAL) + S = Scope::Hidden; + else if (Global) + S = Scope::Default; + // TODO: map all symbols for c++ static initialization to SideEffectOnly + + Linkage L = Weak ? Linkage::Weak : Linkage::Strong; + auto SymbolAddr = Symbol.getAddress(); + if (!SymbolAddr) + return SymbolAddr.takeError(); + auto IsCallableOrErr = Symbol.isFunction(); + if (!IsCallableOrErr) + return IsCallableOrErr.takeError(); + + auto BlockOffset = *SymbolAddr - B->getAddress().getValue(); + + LLVM_DEBUG(dbgs() << " creating with linkage = " << getLinkageName(L) + << ", scope = " << getScopeName(S) << ", B = " + << format_hex(B->getAddress().getValue(), 16) << "\n"); + + SymbolIndexTable[SymbolIndex] = + &G->addDefinedSymbol(*B, BlockOffset, *SymbolName, Symbol.getSize(), L, + S, *IsCallableOrErr, true); + } + + return Error::success(); +} + +Error XCOFFLinkGraphBuilder::processRelocations() { + LLVM_DEBUG(dbgs() << " Creating relocations...\n"); + + for (object::SectionRef Section : Obj.sections()) { + auto SectionName = Section.getName(); + if (!SectionName) + return SectionName.takeError(); + + LLVM_DEBUG(dbgs() << " Relocations for section " << *SectionName + << ":\n"); + + for (object::RelocationRef Relocation : Section.relocations()) { + SmallString<16> RelocName; + Relocation.getTypeName(RelocName); + object::SymbolRef Symbol = *Relocation.getSymbol(); + + auto TargetSymbol = Symbol.getName(); + if (!TargetSymbol) + return TargetSymbol.takeError(); + + auto SymbolIndex = Obj.getSymbolIndex(Symbol.getRawDataRefImpl().p); + + LLVM_DEBUG(dbgs() << " " << format_hex(Relocation.getOffset(), 16) + << " (idx: " << SymbolIndex << ")" + << " " << RelocName << " " << *TargetSymbol << "\n";); + + assert(SymbolIndexTable.contains(SymbolIndex) && + "Relocation needs a record in the symbol table"); + auto *S = SymbolIndexTable[SymbolIndex]; + auto It = find_if(G->blocks(), + [Target = orc::ExecutorAddr(Section.getAddress() + + Relocation.getOffset())]( + const Block *B) -> bool { + return B->getRange().contains(Target); + }); + assert(It != G->blocks().end() && + "Cannot find the target relocation block"); + Block *B = *It; + + auto TargetBlockOffset = Section.getAddress() + Relocation.getOffset() - + B->getAddress().getValue(); + switch (Relocation.getType()) { + case XCOFF::R_POS: + B->addEdge(ppc64::EdgeKind_ppc64::Pointer64, TargetBlockOffset, *S, 0); + break; + default: + SmallString<16> RelocType; + Relocation.getTypeName(RelocType); + return make_error( + "Unsupported Relocation Type: " + RelocType, std::error_code()); + } + } + } + + return Error::success(); +} + +Expected> XCOFFLinkGraphBuilder::buildGraph() { + LLVM_DEBUG(dbgs() << "Building XCOFFLinkGraph...\n"); + + // FIXME: Check to make sure the object is relocatable + + if (auto Err = processSections()) + return Err; + if (auto Err = processCsectsAndSymbols()) + return Err; + if (auto Err = processRelocations()) + return Err; + + return std::move(G); +} + +} // namespace jitlink +} // namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.h new file mode 100644 index 0000000000000..c6481170637c2 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.h @@ -0,0 +1,63 @@ +//===----- XCOFFLinkGraphBuilder.h - XCOFF LinkGraph builder ----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic XCOFF LinkGraph building code. +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_EXECUTIONENGINE_JITLINK_XCOFFLINKGRAPHBUILDER_H +#define LIB_EXECUTIONENGINE_JITLINK_XCOFFLINKGRAPHBUILDER_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/TargetParser/SubtargetFeature.h" +#include + +namespace llvm { +namespace jitlink { + +class XCOFFLinkGraphBuilder { +public: + virtual ~XCOFFLinkGraphBuilder() = default; + Expected> buildGraph(); + +public: + XCOFFLinkGraphBuilder(const object::XCOFFObjectFile &Obj, + std::shared_ptr SSP, Triple TT, + SubtargetFeatures Features, + LinkGraph::GetEdgeKindNameFunction GetEdgeKindName); + LinkGraph &getGraph() const { return *G; } + const object::XCOFFObjectFile &getObject() const { return Obj; } + +private: + Error processSections(); + Error processCsectsAndSymbols(); + Error processRelocations(); + +private: + const object::XCOFFObjectFile &Obj; + std::unique_ptr G; + + Section *UndefSection; + + struct SectionEntry { + jitlink::Section *Section; + object::SectionRef SectionData; + }; + + DenseMap SectionTable; + DenseMap CsectTable; + DenseMap SymbolIndexTable; +}; + +} // namespace jitlink +} // namespace llvm + +#endif // LIB_EXECUTIONENGINE_JITLINK_XCOFFLINKGRAPHBUILDER_H diff --git a/llvm/lib/ExecutionEngine/JITLink/XCOFF_ppc64.cpp b/llvm/lib/ExecutionEngine/JITLink/XCOFF_ppc64.cpp new file mode 100644 index 0000000000000..fd6b5f61749b5 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/XCOFF_ppc64.cpp @@ -0,0 +1,121 @@ +//===------- XCOFF_ppc64.cpp -JIT linker implementation for XCOFF/ppc64 +//-------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// XCOFF/ppc64 jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h" +#include "JITLinkGeneric.h" +#include "XCOFFLinkGraphBuilder.h" +#include "llvm/ADT/bit.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/ppc64.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { + +Expected> createLinkGraphFromXCOFFObject_ppc64( + MemoryBufferRef ObjectBuffer, std::shared_ptr SSP) { + LLVM_DEBUG({ + dbgs() << "Building jitlink graph for new input " + << ObjectBuffer.getBufferIdentifier() << "...\n"; + }); + + auto Obj = object::ObjectFile::createObjectFile(ObjectBuffer); + if (!Obj) + return Obj.takeError(); + assert((**Obj).isXCOFF() && "Expects and XCOFF Object"); + + auto Features = (*Obj)->getFeatures(); + if (!Features) + return Features.takeError(); + LLVM_DEBUG({ + dbgs() << " Features: "; + (*Features).print(dbgs()); + }); + + return XCOFFLinkGraphBuilder(cast(**Obj), + std::move(SSP), Triple("powerpc64-ibm-aix"), + std::move(*Features), ppc64::getEdgeKindName) + .buildGraph(); +} + +class XCOFFJITLinker_ppc64 : public JITLinker { + using JITLinkerBase = JITLinker; + friend JITLinkerBase; + +public: + XCOFFJITLinker_ppc64(std::unique_ptr Ctx, + std::unique_ptr G, + PassConfiguration PassConfig) + : JITLinkerBase(std::move(Ctx), std::move(G), std::move(PassConfig)) { + // FIXME: Post allocation pass define TOC base, this is temporary to support + // building until we can build the required toc entries + defineTOCSymbol(getGraph()); + } + + Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { + LLVM_DEBUG(dbgs() << " Applying fixup for " << G.getName() + << ", address = " << B.getAddress() + << ", target = " << E.getTarget().getName() << ", kind = " + << ppc64::getEdgeKindName(E.getKind()) << "\n"); + switch (E.getKind()) { + case ppc64::Pointer64: + if (auto Err = ppc64::applyFixup(G, B, E, TOCSymbol)) + return Err; + break; + default: + return make_error("Unsupported relocation type", + std::error_code()); + } + return Error::success(); + } + +private: + void defineTOCSymbol(LinkGraph &G) { + for (Symbol *S : G.defined_symbols()) { + if (S->hasName() && *S->getName() == StringRef("TOC")) { + TOCSymbol = S; + return; + } + } + llvm_unreachable("LinkGraph does not contan an TOC Symbol"); + } + +private: + Symbol *TOCSymbol = nullptr; +}; + +void link_XCOFF_ppc64(std::unique_ptr G, + std::unique_ptr Ctx) { + // Ctx->notifyFailed(make_error( + // "link_XCOFF_ppc64 is not implemented", std::error_code())); + + PassConfiguration Config; + + // Pass insertions + + if (auto Err = Ctx->modifyPassConfig(*G, Config)) + return Ctx->notifyFailed(std::move(Err)); + + XCOFFJITLinker_ppc64::link(std::move(Ctx), std::move(G), std::move(Config)); +} + +} // namespace jitlink +} // namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp b/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp index 77ae7c7ca2e0e..4f01c01da4b9f 100644 --- a/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp @@ -25,6 +25,13 @@ checkCOFFRelocatableObject(std::unique_ptr Obj, return std::move(Obj); } +static Expected> +checkXCOFFRelocatableObject(std::unique_ptr Obj, + const Triple &TT) { + // TODO: Actually check the architecture of the file. + return std::move(Obj); +} + static Expected> checkELFRelocatableObject(std::unique_ptr Obj, const Triple &TT) { // TODO: Actually check the architecture of the file. @@ -105,6 +112,15 @@ loadLinkableFile(StringRef Path, const Triple &TT, LoadArchives LA, return loadLinkableSliceFromMachOUniversalBinary( FD, std::move(*Buf), TT, LA, Path, *IdentifierOverride); break; + case file_magic::xcoff_object_64: + if (!RequireFormat || *RequireFormat == Triple::XCOFF) { + auto CheckedBuf = checkXCOFFRelocatableObject(std::move(*Buf), TT); + if (!CheckedBuf) + return CheckedBuf.takeError(); + return std::make_pair(std::move(*CheckedBuf), + LinkableFileKind::RelocatableObject); + } + break; default: break; } diff --git a/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp b/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp index 0b2cafb0bff13..b9cee98d45d77 100644 --- a/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp @@ -7,11 +7,13 @@ //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h" +#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/XCOFFObjectFile.h" #include #define DEBUG_TYPE "orc" @@ -227,6 +229,52 @@ getCOFFObjectFileSymbolInfo(ExecutionSession &ES, return I; } +Expected +getXCOFFObjectFileSymbolInfo(ExecutionSession &ES, + const object::ObjectFile &Obj) { + + MaterializationUnit::Interface I; + + for (auto &Sym : Obj.symbols()) { + Expected SymFlagsOrErr = Sym.getFlags(); + if (!SymFlagsOrErr) + return SymFlagsOrErr.takeError(); + uint32_t Flags = *SymFlagsOrErr; + + // Skip undefined, non global and ST_File + if (Flags & object::SymbolRef::SF_Undefined) + continue; + if (!(Flags & object::SymbolRef::SF_Global)) + continue; + + auto SymbolType = Sym.getType(); + if (!SymbolType) + return SymbolType.takeError(); + + if (*SymbolType == object::SymbolRef::ST_File) + continue; + + auto Name = Sym.getName(); + if (!Name) + return Name.takeError(); + auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym); + if (!SymFlags) + return SymFlags.takeError(); + + // TODO: Revisit symbol visibility + // On AIX, symbols with C_EXT and C_WEAKEXT symbols have no specified + // visibility are considered to have Default scope for LinkGraph. When the + // object is not a DSO, symbol visibility is not specified. In the absence + // of an Export List, its reasonable to minimic roughly the behaviour of + // -bexpall or CreateExportList. + *SymFlags |= JITSymbolFlags::Exported; + + I.SymbolFlags[ES.intern(std::move(*Name))] = std::move(*SymFlags); + } + // TODO: Find all initialization symbols for c++ static initializers + return I; +} + Expected getGenericObjectFileSymbolInfo(ExecutionSession &ES, const object::ObjectFile &Obj) { @@ -280,6 +328,8 @@ getObjectFileInterface(ExecutionSession &ES, MemoryBufferRef ObjBuffer) { return getELFObjectFileSymbolInfo(ES, *ELFObj); else if (auto *COFFObj = dyn_cast(Obj->get())) return getCOFFObjectFileSymbolInfo(ES, *COFFObj); + else if (auto *XCOFFObj = dyn_cast(Obj->get())) + return getXCOFFObjectFileSymbolInfo(ES, *XCOFFObj); return getGenericObjectFileSymbolInfo(ES, **Obj); } diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp index df3b2a091aec2..5a246438e2c0e 100644 --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -429,9 +429,13 @@ XCOFFObjectFile::getSectionContents(DataRefImpl Sec) const { } uint64_t XCOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const { - uint64_t Result = 0; - llvm_unreachable("Not yet implemented!"); - return Result; + // TODO: Copied from MC/XCOFFObjectWriter.cpp + // Sections other than DWARF section use DefaultSectionAlign as the default + // alignment, while DWARF sections have their own alignments. DWARF section + // alignment is bigger than DefaultSectionAlign. + if (isDebugSection(Sec)) + return 8; + return 4; } uint64_t XCOFFObjectFile::getSectionFileOffsetToRawData(DataRefImpl Sec) const { diff --git a/llvm/test/ExecutionEngine/JITLink/ppc64/XCOFF_ppc64.ll b/llvm/test/ExecutionEngine/JITLink/ppc64/XCOFF_ppc64.ll new file mode 100644 index 0000000000000..659b0a8959e73 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/ppc64/XCOFF_ppc64.ll @@ -0,0 +1,24 @@ +; AIX's support for llvm-mc does not have enough support for directives like .csect +; so we can't use the tool. llvm-jitlink -check is not available as it requries +; implementation of registerXCOFFGraphInfo. Will revisit this testcase once support +; is more complete. + +; RUN: mkdir -p %t +; RUN: llc --filetype=obj -o %t/xcoff_ppc64.o %s +; RUN: llvm-jitlink -noexec -num-threads=0 -triple=powerpc64-ibm-aix %t/xcoff_ppc64.o + +target datalayout = "E-m:a-Fi64-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512" +target triple = "powerpc64-ibm-aix" + +define i32 @main() #0 { +entry: + ret i32 0 +} + +attributes #0 = { "target-cpu"="pwr7" } + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 8, !"PIC Level", i32 2} +