diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_arm.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_arm.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_arm.h @@ -0,0 +1,37 @@ +//===--- ELF_arm.h - JIT link functions for 32-bit arm/thumb ---*- 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 ELF/arm. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_ARM_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_ARM_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// Create a LinkGraph from an ELF/arm 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> +createLinkGraphFromELFObject_arm(MemoryBufferRef ObjectBuffer); + +/// jit-link the given object buffer, which must be an ELF arm/thumb object +/// file. +void link_ELF_arm(std::unique_ptr G, + std::unique_ptr Ctx); + +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_ARM_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h @@ -460,7 +460,17 @@ orc::ExecutorAddrDiff Offset, StringRef Name, orc::ExecutorAddrDiff Size, Linkage L, Scope S, bool IsLive, bool IsCallable) { - assert((Offset + Size) <= Base.getSize() && + // + // On ARM the least significant bit (LSB) in a branch offset tells the CPU + // whether the subsequent code is ARM (LSB=0) or Thumb (LSB=1). It's not + // part of the range, but we need to maintain the exact value. This can + // break the below assertion. + // + // FIXME: Named definitions will rarely be entirely unaligned, so it might + // be acceptable to ignore the LSB for the size assertion here, until we + // find a better solution. + // + assert(((Offset & ~0x1) + Size) <= Base.getSize() && "Symbol extends past end of block"); assert(!Name.empty() && "Name cannot be empty"); auto *Sym = Allocator.Allocate(); diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/arm.h b/llvm/include/llvm/ExecutionEngine/JITLink/arm.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/arm.h @@ -0,0 +1,261 @@ +//===-- arm.h - Generic JITLink arm/thumb edge kinds, utilities -*- 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 utilities for graphs representing arm/thumb objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ARM_H +#define LLVM_EXECUTIONENGINE_JITLINK_ARM_H + +#include "TableManager.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include "llvm/Support/ARMBuildAttributes.h" + +namespace llvm { +namespace jitlink { +namespace arm { + +/// ARM/Thumb fixup types. +enum EdgeKind_arm : Edge::Kind { + /// + /// List of relocations applicable in ARM mode + /// + FirstArmRelocation = Edge::FirstRelocation, + + /// Plain 32-bit pointer relocation; optional addend is stored in-place + Arm_Delta32 = FirstArmRelocation, + + /// + /// List of relocations applicable in Thumb mode + /// + FirstThumbRelocation, + + /// PC-relative call instruction (bridges between ARM and Thumb) + Thumb_Call = FirstThumbRelocation, + + /// PC-relative jump instruction + Thumb_Jump24, + + /// Write immediate value to the lower halfword of the destination register + Thumb_MovwAbsNC, + + /// Write immediate value to the top halfword of the destination register + Thumb_MovtAbs, +}; + +/// Human-readable name for a given arm/thumb edge +const char *getEdgeKindName(Edge::Kind K); + +/// Human-readable name for a given ARM CPU architecture kind +const char *getCPUArchName(ARMBuildAttrs::CPUArch K); + +/// Implementation of ARM branch range extension stubs ("veneers") vary with +/// ISA kinds (arm/thumb/interworking), CPU architectures (v4, v6, v7) and +/// branch types (absolute/PC-relative). +enum VeneerKind { + Unsupported = 0, + Thumbv7, +}; + +/// JITLink sub-arch configuration for ARM CPU models +struct ArmConfig { + bool HasBlx = false; + bool HasMovtMovw = false; + bool J1J2BranchEncoding = false; + VeneerKind Veneers = Unsupported; +}; + +/// Obtain the sub-arch configuration for a given ARM CPU model. +inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) { + ArmConfig ArmCfg; + switch (CPUArch) { + case ARMBuildAttrs::v7: + case ARMBuildAttrs::v8_A: + ArmCfg.HasBlx = true; + ArmCfg.J1J2BranchEncoding = true; + ArmCfg.HasMovtMovw = true; + ArmCfg.Veneers = Thumbv7; + break; + default: + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Warning: ARM config not defined for CPU architecture " + << getCPUArchName(CPUArch); + }); + break; + } + return ArmCfg; +} + +/// Arm and Thumb branches have a PC bias of 8 and 4 respectively. +inline int64_t getPCBias(Edge::Kind K) { + switch (K) { + case Thumb_Call: + case Thumb_Jump24: + return 4; + default: + return 8; + } +} + +/// Returns extracted bits Val[Hi:Lo]. +inline uint32_t extractBits(uint32_t Val, unsigned Hi, unsigned Lo) { + return (Val & (((1UL << (Hi + 1)) - 1))) >> Lo; +} + +/// Apply fixup expression for edge to block content. +inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, + const ArmConfig &ArmCfg) { + using namespace support; + + char *BlockWorkingMem = B.getAlreadyMutableContent().data(); + char *FixupPtr = BlockWorkingMem + E.getOffset(); + uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue(); + uint64_t TargetAddress = E.getTarget().getAddress().getValue(); + + switch (E.getKind()) { + case Arm_Delta32: { + // Implicit addend is stored in-place. + int64_t Addend = SignExtend64<32>(*(little32_t *)FixupPtr); + int64_t Value = TargetAddress - FixupAddress + Addend; + if (!isInt<32>(Value)) + return makeTargetOutOfRangeError(G, B, E); + *(little32_t *)FixupPtr = Value; + break; + } + case Thumb_Jump24: { + if ((TargetAddress & 0x1) != 0x1) + return make_error("Branch instruction needs interworking " + "veneer when bridging to ARM: " + + StringRef(getEdgeKindName(E.getKind()))); + [[fallthrough]]; + } + case Thumb_Call: { + int64_t Addend = -getPCBias(E.getKind()); + int64_t Value = TargetAddress - FixupAddress + Addend; + + // Least significant byte denotes Thumb state and is not part of the range. + int64_t Distance = Value & ~0x1; + if (ArmCfg.J1J2BranchEncoding ? !isInt<25>(Distance) : !isInt<23>(Distance)) + return makeTargetOutOfRangeError(G, B, E); + + // Value = S:J1:J2:Imm10:Imm11:0 + uint16_t S = extractBits(Value, /*Hi=*/14, /*Lo=*/14); + uint16_t J1 = (((~(Value >> 10)) ^ (Value >> 11)) & 0x2000); + uint16_t J2 = (((~(Value >> 11)) ^ (Value >> 13)) & 0x0800); + uint16_t Imm10 = extractBits(Value, /*Hi=*/11, /*Lo=*/1); + uint16_t Imm11 = extractBits(Value, /*Hi=*/23, /*Lo=*/12); + + uint16_t Opcode = 0xf000; + uint16_t RawLo = 0xd000 & *(ulittle16_t *)(FixupPtr + 2); + + uint32_t Hi = Opcode | S << 10 | Imm11; + uint32_t Lo = RawLo | J1 | J2 | Imm10; + *(ulittle32_t *)FixupPtr = Hi | Lo << 16; + break; + } + case Thumb_MovwAbsNC: { + /// Value = imm4:imm1:imm3:imm8 + int64_t Value = TargetAddress; + uint16_t Imm4 = extractBits(Value, /*Hi=*/15, /*Lo=*/12); + uint16_t Imm1 = extractBits(Value, /*Hi=*/11, /*Lo=*/11); + uint16_t Imm3 = extractBits(Value, /*Hi=*/10, /*Lo=*/8); + uint16_t Imm8 = extractBits(Value, /*Hi=*/7, /*Lo=*/0); + + uint16_t Opcode = 0xf240; + uint16_t RawLo = 0x8f00 & *(ulittle16_t *)(FixupPtr + 2); + + uint32_t Hi = Opcode | Imm1 << 10 | Imm4; + uint32_t Lo = RawLo | Imm3 << 12 | Imm8; + *(ulittle32_t *)FixupPtr = Hi | Lo << 16; + break; + } + case Thumb_MovtAbs: { + /// Value = imm4:imm1:imm3:imm8 + int64_t Value = TargetAddress; + uint16_t Imm4 = extractBits(Value, /*Hi=*/31, /*Lo=*/28); + uint16_t Imm1 = extractBits(Value, /*Hi=*/27, /*Lo=*/27); + uint16_t Imm3 = extractBits(Value, /*Hi=*/26, /*Lo=*/24); + uint16_t Imm8 = extractBits(Value, /*Hi=*/23, /*Lo=*/16); + + uint16_t Opcode = 0xf2c0; + uint16_t RawLo = 0x8f00 & *(ulittle16_t *)(FixupPtr + 2); + + uint32_t Hi = Opcode | Imm1 << 10 | Imm4; + uint32_t Lo = RawLo | Imm3 << 12 | Imm8; + *(ulittle32_t *)FixupPtr = Hi | Lo << 16; + break; + } + default: + return make_error( + "In graph " + G.getName() + ", section " + B.getSection().getName() + + " unsupported edge kind " + getEdgeKindName(E.getKind())); + } + + return Error::success(); +} + +/// Branch range extension stubs builder for a concrete veneer kind. +/// Let's keep it simple for the moment and not wire this through a GOT. +template +class VeneersManager : public TableManager> { +public: + VeneersManager() = default; + + static StringRef getSectionName() { return "Veneer$$Code"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + if (E.getTarget().isDefined()) + return false; + + switch (E.getKind()) { + case arm::Thumb_Call: + case arm::Thumb_Jump24: { + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " + << B->getFixupAddress(E) << " (" << B->getAddress() << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"; + }); + E.setTarget(this->getEntryForTarget(G, E.getTarget())); + return true; + } + } + return false; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target); + +private: + template + Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size], + uint64_t Alignment) { + ArrayRef Template(reinterpret_cast(Code), Size); + return G.createContentBlock(getStubsSection(G), Template, + orc::ExecutorAddr(), Alignment, 0); + } + + Section &getStubsSection(LinkGraph &G) { + if (!StubsSection) + StubsSection = &G.createSection(getSectionName(), + orc::MemProt::Read | orc::MemProt::Exec); + return *StubsSection; + } + + Section *StubsSection = nullptr; +}; + +/// Define an explicit specialization for each veneer kind. +template <> +Symbol &VeneersManager::createEntry(LinkGraph &G, Symbol &Target); + +} // namespace arm +} // namespace jitlink +} // namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ARM_H diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -21,6 +21,7 @@ ELF.cpp ELFLinkGraphBuilder.cpp ELF_aarch64.cpp + ELF_arm.cpp ELF_i386.cpp ELF_loongarch.cpp ELF_riscv.cpp @@ -34,6 +35,7 @@ # Architectures: aarch64.cpp + arm.cpp i386.cpp loongarch.cpp riscv.cpp diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp --- a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp @@ -14,6 +14,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/ExecutionEngine/JITLink/ELF_aarch64.h" +#include "llvm/ExecutionEngine/JITLink/ELF_arm.h" #include "llvm/ExecutionEngine/JITLink/ELF_i386.h" #include "llvm/ExecutionEngine/JITLink/ELF_loongarch.h" #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h" @@ -69,6 +70,8 @@ switch (*TargetMachineArch) { case ELF::EM_AARCH64: return createLinkGraphFromELFObject_aarch64(ObjectBuffer); + case ELF::EM_ARM: + return createLinkGraphFromELFObject_arm(ObjectBuffer); case ELF::EM_LOONGARCH: return createLinkGraphFromELFObject_loongarch(ObjectBuffer); case ELF::EM_RISCV: @@ -90,6 +93,10 @@ case Triple::aarch64: link_ELF_aarch64(std::move(G), std::move(Ctx)); return; + case Triple::arm: + case Triple::thumb: + link_ELF_arm(std::move(G), std::move(Ctx)); + return; case Triple::loongarch32: case Triple::loongarch64: link_ELF_loongarch(std::move(G), std::move(Ctx)); diff --git a/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h --- a/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h +++ b/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h @@ -108,6 +108,10 @@ Error graphifySections(); Error graphifySymbols(); + /// Override in derived classes to suppress certain sections in the link + /// graph. + virtual bool excludeSectionByName(StringRef Name) const { return false; } + /// Traverse all matching ELFT::Rela relocation records in the given section. /// The handler function Func should be callable with this signature: /// Error(const typename ELFT::Rela &, @@ -307,6 +311,13 @@ auto Name = Obj.getSectionName(Sec, SectionStringTab); if (!Name) return Name.takeError(); + if (excludeSectionByName(*Name)) { + LLVM_DEBUG({ + dbgs() << " " << SecIndex << ": Skipping section \"" << *Name + << "\"\n"; + }); + continue; + } // If the name indicates that it's a debug section then skip it: We don't // support those yet. diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_arm.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_arm.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_arm.cpp @@ -0,0 +1,244 @@ +//===-- ELF_arm.cpp - JIT linker implementation for 32-bit arm and thumb --===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// ELF/arm jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ELF_arm.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/arm.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/TargetParser/ARMTargetParser.h" + +#include "EHFrameSupportImpl.h" +#include "ELFLinkGraphBuilder.h" +#include "JITLinkGeneric.h" + +#define DEBUG_TYPE "jitlink" + +using namespace llvm; +using namespace llvm::jitlink; +using namespace llvm::jitlink::arm; + +namespace { + +class ELFJITLinker_arm : public JITLinker { + friend class JITLinker; + +public: + ELFJITLinker_arm(std::unique_ptr Ctx, + std::unique_ptr G, PassConfiguration PassCfg, + ArmConfig ArmCfg) + : JITLinker(std::move(Ctx), std::move(G), std::move(PassCfg)), + ArmCfg(std::move(ArmCfg)) {} + +private: + ArmConfig ArmCfg; + + Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { + return arm::applyFixup(G, B, E, ArmCfg); + } +}; + +template +class ELFLinkGraphBuilder_arm : public ELFLinkGraphBuilder { +private: + static Expected getRelocationKind(const uint32_t Type) { + using namespace arm; + switch (Type) { + case ELF::R_ARM_REL32: + return Arm_Delta32; + case ELF::R_ARM_THM_CALL: + return Thumb_Call; + case ELF::R_ARM_THM_JUMP24: + return Thumb_Jump24; + case ELF::R_ARM_THM_MOVW_ABS_NC: + return Thumb_MovwAbsNC; + case ELF::R_ARM_THM_MOVT_ABS: + return Thumb_MovtAbs; + } + + return make_error( + "Unsupported arm relocation:" + formatv("{0:d}: ", Type) + + object::getELFRelocationTypeName(ELF::EM_ARM, Type)); + } + + bool excludeSectionByName(StringRef Name) const override { + // TODO: Process .ex* sections + return Name.contains(".ARM.exidx") || Name.contains(".ARM.extab"); + } + + Error addRelocations() override { + LLVM_DEBUG(dbgs() << "Processing relocations:\n"); + + using Base = ELFLinkGraphBuilder; + using Self = ELFLinkGraphBuilder_arm; + for (const auto &RelSect : Base::Sections) { + auto Name = Base::Obj.getSectionName(RelSect, Base::SectionStringTab); + assert(Name && "We traversed section names before in graphifySections()"); + if (Name && !excludeSectionByName(*Name)) + if (Error Err = Base::forEachRelRelocation(RelSect, this, + &Self::addSingleRelocation)) + return Err; + } + return Error::success(); + } + + Error addSingleRelocation(const typename ELFT::Rel &Rel, + const typename ELFT::Shdr &FixupSect, + Block &BlockToFix) { + using Base = ELFLinkGraphBuilder; + + uint32_t SymbolIndex = Rel.getSymbol(false); + auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec); + if (!ObjSymbol) + return ObjSymbol.takeError(); + + Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex); + if (!GraphSymbol) + return make_error( + formatv("Could not find symbol at given index, did you add it to " + "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}", + SymbolIndex, (*ObjSymbol)->st_shndx, + Base::GraphSymbols.size()), + inconvertibleErrorCode()); + + uint32_t Type = Rel.getType(false); + Expected Kind = getRelocationKind(Type); + if (!Kind) + return Kind.takeError(); + + int64_t Addend = 0; // 'Rel' relocs read implicit addend from fixup address + auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset; + Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); + Edge GE(*Kind, Offset, *GraphSymbol, Addend); + LLVM_DEBUG({ + dbgs() << " "; + printEdge(dbgs(), BlockToFix, GE, arm::getEdgeKindName(*Kind)); + dbgs() << "\n"; + }); + + BlockToFix.addEdge(std::move(GE)); + return Error::success(); + } + +public: + ELFLinkGraphBuilder_arm(StringRef FileName, const object::ELFFile &Obj, + Triple TT) + : ELFLinkGraphBuilder(Obj, std::move(TT), FileName, + arm::getEdgeKindName) {} +}; + +template Error buildTables_ELF_arm(LinkGraph &G) { + LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); + + arm::VeneersManager PLT; + visitExistingEdges(G, PLT); + return Error::success(); +} + +} // namespace + +namespace llvm { +namespace jitlink { + +Expected> +createLinkGraphFromELFObject_arm(MemoryBufferRef ObjectBuffer) { + LLVM_DEBUG({ + dbgs() << "Building jitlink graph for new input " + << ObjectBuffer.getBufferIdentifier() << "...\n"; + }); + + auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer); + if (!ELFObj) + return ELFObj.takeError(); + + auto TT = (*ELFObj)->makeTriple(); + ARM::ArchKind AK = ARM::parseArch(TT.getArchName()); + if (AK == ARM::ArchKind::INVALID) + return make_error( + "Failed to build ELF link graph: Invalid ARM ArchKind"); + + using namespace ARMBuildAttrs; + auto Arch = static_cast(ARM::getArchAttr(AK)); + switch (Arch) { + case v7: + case v8_A: + assert(getArmConfigForCPUArch(Arch).Veneers != arm::Unsupported && + "Provide a ARM config for each supported CPU"); + break; + case Pre_v4: + case v4: + case v4T: + case v5T: + case v5TE: + case v5TEJ: + case v6: + case v6KZ: + case v6T2: + case v6K: + case v6_M: + case v6S_M: + case v7E_M: + case v8_R: + case v8_M_Base: + case v8_M_Main: + case v8_1_M_Main: + case v9_A: + return make_error( + "Failed to build ELF link graph: Unsupported CPU sub-arch " + + StringRef(arm::getCPUArchName(Arch))); + } + + auto &ELFObjFile = cast>(**ELFObj); + return ELFLinkGraphBuilder_arm((*ELFObj)->getFileName(), + ELFObjFile.getELFFile(), TT) + .buildGraph(); +} + +void link_ELF_arm(std::unique_ptr G, + std::unique_ptr Ctx) { + const Triple &TT = G->getTargetTriple(); + + using namespace ARMBuildAttrs; + ARM::ArchKind AK = ARM::parseArch(TT.getArchName()); + auto CPU = static_cast(ARM::getArchAttr(AK)); + arm::ArmConfig ArmCfg = getArmConfigForCPUArch(CPU); + + PassConfiguration PassCfg; + if (Ctx->shouldAddDefaultTargetPasses(TT)) { + // Add a mark-live pass. + if (auto MarkLive = Ctx->getMarkLivePass(TT)) + PassCfg.PrePrunePasses.push_back(std::move(MarkLive)); + else + PassCfg.PrePrunePasses.push_back(markAllSymbolsLive); + + // ARM branch range extension stubs are commonly called veneers. + switch (ArmCfg.Veneers) { + case arm::Thumbv7: + PassCfg.PostPrunePasses.push_back(buildTables_ELF_arm); + break; + case arm::Unsupported: + llvm_unreachable("Check before building graph"); + } + } + + if (auto Err = Ctx->modifyPassConfig(*G, PassCfg)) + return Ctx->notifyFailed(std::move(Err)); + + ELFJITLinker_arm::link(std::move(Ctx), std::move(G), std::move(PassCfg), + std::move(ArmCfg)); +} + +} // namespace jitlink +} // namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/arm.cpp b/llvm/lib/ExecutionEngine/JITLink/arm.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/arm.cpp @@ -0,0 +1,88 @@ +//===----- arm.cpp - Generic JITLink arm/thumb edge kinds, utilities ------===// +// +// 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 utilities for graphs representing arm/thumb objects. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/arm.h" +#include "llvm/BinaryFormat/ELF.h" + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { +namespace arm { + +const uint8_t Thumbv7ABS[] = { + 0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000 ; lower 16-bit + 0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000 ; upper 16-bit + 0x60, 0x47 // bx r12 +}; + +template <> +Symbol &VeneersManager::createEntry(LinkGraph &G, Symbol &Target) { + constexpr uint64_t Alignment = 4; + Block &B = addStub(G, Thumbv7ABS, Alignment); + B.addEdge(arm::Thumb_MovwAbsNC, 0, Target, 0); + B.addEdge(arm::Thumb_MovtAbs, 4, Target, 0); + return G.addAnonymousSymbol(B, 0, B.getSize(), true, false); +} + +const char *getEdgeKindName(Edge::Kind K) { + switch (K) { + case Arm_Delta32: + return "R_ARM_REL32"; + case Thumb_Call: + return "R_ARM_THM_CALL"; + case Thumb_Jump24: + return "R_ARM_THM_JUMP24"; + case Thumb_MovwAbsNC: + return "R_ARM_THM_MOVW_ABS_NC"; + case Thumb_MovtAbs: + return "R_ARM_THM_MOVT_ABS"; + default: + return getGenericEdgeKindName(K); + } +} + +const char *getCPUArchName(ARMBuildAttrs::CPUArch K) { +#define CPUARCH_NAME_CASE(K) \ + case K: \ + return #K; + + using namespace ARMBuildAttrs; + switch (K) { + CPUARCH_NAME_CASE(Pre_v4) + CPUARCH_NAME_CASE(v4) + CPUARCH_NAME_CASE(v4T) + CPUARCH_NAME_CASE(v5T) + CPUARCH_NAME_CASE(v5TE) + CPUARCH_NAME_CASE(v5TEJ) + CPUARCH_NAME_CASE(v6) + CPUARCH_NAME_CASE(v6KZ) + CPUARCH_NAME_CASE(v6T2) + CPUARCH_NAME_CASE(v6K) + CPUARCH_NAME_CASE(v7) + CPUARCH_NAME_CASE(v6_M) + CPUARCH_NAME_CASE(v6S_M) + CPUARCH_NAME_CASE(v7E_M) + CPUARCH_NAME_CASE(v8_A) + CPUARCH_NAME_CASE(v8_R) + CPUARCH_NAME_CASE(v8_M_Base) + CPUARCH_NAME_CASE(v8_M_Main) + CPUARCH_NAME_CASE(v8_1_M_Main) + CPUARCH_NAME_CASE(v9_A) + } + llvm_unreachable("Missing CPUArch in switch?"); +#undef CPUARCH_NAME_CASE +} + +} // namespace arm +} // namespace jitlink +} // namespace llvm diff --git a/llvm/test/ExecutionEngine/JITLink/arm/ELF_thumbv7_printf.s b/llvm/test/ExecutionEngine/JITLink/arm/ELF_thumbv7_printf.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/arm/ELF_thumbv7_printf.s @@ -0,0 +1,40 @@ +// RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t.o %s +// RUN: llvm-jitlink -noexec %t.o + + .globl main + .p2align 2 + .type main,%function + .code 16 + .thumb_func +main: + .fnstart + .save {r7, lr} + push {r7, lr} + .setfp r7, sp + mov r7, sp + .pad #8 + sub sp, #8 + movs r0, #0 + str r0, [sp] + str r0, [sp, #4] + ldr r0, .LCPI0_0 +.LPC0_0: + add r0, pc + bl printf + ldr r0, [sp] + add sp, #8 + pop {r7, pc} + + .p2align 2 +.LCPI0_0: + .long .L.str-(.LPC0_0+4) + + .size main, .-main + .cantunwind + .fnend + + .type .L.str,%object + .section .rodata.str1.1,"aMS",%progbits,1 +.L.str: + .asciz "Hello arm!\n" + .size .L.str, 12 diff --git a/llvm/test/ExecutionEngine/JITLink/arm/lit.local.cfg b/llvm/test/ExecutionEngine/JITLink/arm/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/arm/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'ARM' in config.root.targets: + config.unsupported = True