// 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))) {} #ifndef NDEBUG 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)"; } llvm_unreachable("Unknown XCOFF::StorageClass enum"); } #endif 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); } #ifndef NDEBUG 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"; } #endif Error XCOFFLinkGraphBuilder::processCsectsAndSymbols() { LLVM_DEBUG(dbgs() << " Creating graph blocks and symbols...\n"); for ([[maybe_unused]] 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}; 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