
ArrayRef now has a new constructor that takes a parameter whose type has data() and size(). This patch migrates: ArrayRef<T>(X.data(), X.size() to: ArrayRef<T>(X)
421 lines
15 KiB
C++
421 lines
15 KiB
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.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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),
|
|
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
|