| // 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 <memory> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "jitlink" | |
| namespace llvm { | |
| namespace jitlink { | |
| XCOFFLinkGraphBuilder::XCOFFLinkGraphBuilder( | |
| const object::XCOFFObjectFile &Obj, | |
| std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT, | |
| SubtargetFeatures Features, | |
| LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) | |
| : Obj(Obj), G(std::make_unique<LinkGraph>( | |
| std::string(Obj.getFileName()), std::move(SSP), | |
| std::move(TT), Obj.getBytesInAddress(), 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<object::XCOFFSymbolRef> | |
| getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile &Obj, | |
| const object::SymbolRef &Sym) { | |
| const object::XCOFFSymbolRef SymRef = | |
| Obj.toSymbolRef(Sym.getRawDataRefImpl()); | |
| if (!SymRef.isCsectSymbol()) | |
| return std::nullopt; | |
| Expected<object::XCOFFCsectAuxRef> CsectAuxEntOrErr = | |
| SymRef.getXCOFFCsectAuxRef(); | |
| if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel()) | |
| return std::nullopt; | |
| uint32_t Idx = | |
| static_cast<uint32_t>(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<char> 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<StringError>( | |
| "Unsupported Relocation Type: " + RelocType, std::error_code()); | |
| } | |
| } | |
| } | |
| return Error::success(); | |
| } | |
| Expected<std::unique_ptr<LinkGraph>> 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 |