Skip to content

Commit 2ed91da

Browse files
[JITLink] Add initial Aarch64 support
Set up basic infrastructure for 64-bit ARM architecture support in JITLink. It allows for loading a minimal object file and resolving a single relocation. Advanced features like GOT and PLT handling or relaxations were intentionally left out for the moment. This patch follows the idea to keep implementations for ARM (32-bit) and Aaarch64 (64-bit) separate, because: * it might be easier to share code with the MachO "arm64" JITLink backend * LLVM has individual targets for ARM and Aaarch64 as well Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D108986
1 parent 562521e commit 2ed91da

File tree

7 files changed

+371
-2
lines changed

7 files changed

+371
-2
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===--- ELF_aarch64.h - JIT link functions for ELF/aarch64 --*- C++ -*----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
//===----------------------------------------------------------------------===//
10+
//
11+
// jit-link functions for ELF/aarch64.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_AARCH64_H
16+
#define LLVM_EXECUTIONENGINE_JITLINK_ELF_AARCH64_H
17+
18+
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
19+
20+
namespace llvm {
21+
namespace jitlink {
22+
23+
/// Create a LinkGraph from an ELF/aarch64 relocatable object
24+
///
25+
/// Note: The graph does not take ownership of the underlying buffer, nor copy
26+
/// its contents. The caller is responsible for ensuring that the object buffer
27+
/// outlives the graph.
28+
Expected<std::unique_ptr<LinkGraph>>
29+
createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer);
30+
31+
/// jit-link the given object buffer, which must be a ELF aarch64 relocatable
32+
/// object file.
33+
void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
34+
std::unique_ptr<JITLinkContext> Ctx);
35+
36+
} // end namespace jitlink
37+
} // end namespace llvm
38+
39+
#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_AARCH64_H
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//=== aarch64.h - Generic JITLink aarch64 edge kinds, utilities -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Generic utilities for graphs representing aarch64 objects.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H
14+
#define LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H
15+
16+
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
17+
18+
namespace llvm {
19+
namespace jitlink {
20+
namespace aarch64 {
21+
22+
/// Represets aarch64 fixups
23+
enum EdgeKind_aarch64 : Edge::Kind {
24+
25+
/// Set a CALL immediate field to bits [27:2] of X = Target - Fixup + Addend
26+
R_AARCH64_CALL26 = Edge::FirstRelocation,
27+
28+
};
29+
30+
/// Returns a string name for the given aarch64 edge. For debugging purposes
31+
/// only
32+
const char *getEdgeKindName(Edge::Kind K);
33+
34+
} // namespace aarch64
35+
} // namespace jitlink
36+
} // namespace llvm
37+
38+
#endif // LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H

llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ add_llvm_component_library(LLVMJITLink
1313
MachOLinkGraphBuilder.cpp
1414

1515
# ELF
16-
1716
ELF.cpp
1817
ELFLinkGraphBuilder.cpp
18+
ELF_aarch64.cpp
1919
ELF_riscv.cpp
2020
ELF_x86_64.cpp
2121

2222
# Architectures:
23+
aarch64.cpp
2324
riscv.cpp
2425
x86_64.cpp
2526

@@ -35,7 +36,7 @@ add_llvm_component_library(LLVMJITLink
3536
OrcTargetProcess
3637
Support
3738
)
38-
39+
3940
target_link_libraries(LLVMJITLink
4041
PRIVATE
4142
LLVMObject

llvm/lib/ExecutionEngine/JITLink/ELF.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/ExecutionEngine/JITLink/ELF.h"
1414

1515
#include "llvm/BinaryFormat/ELF.h"
16+
#include "llvm/ExecutionEngine/JITLink/ELF_aarch64.h"
1617
#include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
1718
#include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h"
1819
#include "llvm/Object/ELF.h"
@@ -64,6 +65,8 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) {
6465
return TargetMachineArch.takeError();
6566

6667
switch (*TargetMachineArch) {
68+
case ELF::EM_AARCH64:
69+
return createLinkGraphFromELFObject_aarch64(ObjectBuffer);
6770
case ELF::EM_RISCV:
6871
return createLinkGraphFromELFObject_riscv(ObjectBuffer);
6972
case ELF::EM_X86_64:
@@ -78,6 +81,9 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) {
7881
void link_ELF(std::unique_ptr<LinkGraph> G,
7982
std::unique_ptr<JITLinkContext> Ctx) {
8083
switch (G->getTargetTriple().getArch()) {
84+
case Triple::aarch64:
85+
link_ELF_aarch64(std::move(G), std::move(Ctx));
86+
return;
8187
case Triple::riscv32:
8288
case Triple::riscv64:
8389
link_ELF_riscv(std::move(G), std::move(Ctx));
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
//===----- ELF_aarch64.cpp - JIT linker implementation for ELF/aarch64 ----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// ELF/aarch64 jit-link implementation.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/ExecutionEngine/JITLink/ELF_aarch64.h"
14+
#include "ELFLinkGraphBuilder.h"
15+
#include "JITLinkGeneric.h"
16+
#include "llvm/BinaryFormat/ELF.h"
17+
#include "llvm/ExecutionEngine/JITLink/aarch64.h"
18+
#include "llvm/Object/ELFObjectFile.h"
19+
20+
#define DEBUG_TYPE "jitlink"
21+
22+
using namespace llvm;
23+
using namespace llvm::jitlink;
24+
25+
namespace llvm {
26+
namespace jitlink {
27+
28+
class ELFJITLinker_aarch64 : public JITLinker<ELFJITLinker_aarch64> {
29+
friend class JITLinker<ELFJITLinker_aarch64>;
30+
31+
public:
32+
ELFJITLinker_aarch64(std::unique_ptr<JITLinkContext> Ctx,
33+
std::unique_ptr<LinkGraph> G,
34+
PassConfiguration PassConfig)
35+
: JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
36+
37+
private:
38+
Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
39+
using namespace aarch64;
40+
using namespace llvm::support;
41+
42+
char *BlockWorkingMem = B.getAlreadyMutableContent().data();
43+
char *FixupPtr = BlockWorkingMem + E.getOffset();
44+
JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
45+
switch (E.getKind()) {
46+
case aarch64::R_AARCH64_CALL26: {
47+
assert((FixupAddress & 0x3) == 0 && "Call-inst is not 32-bit aligned");
48+
int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
49+
50+
if (static_cast<uint64_t>(Value) & 0x3)
51+
return make_error<JITLinkError>("Call target is not 32-bit aligned");
52+
53+
if (!fitsRangeSignedInt<27>(Value))
54+
return makeTargetOutOfRangeError(G, B, E);
55+
56+
uint32_t RawInstr = *(little32_t *)FixupPtr;
57+
assert((RawInstr & 0x7fffffff) == 0x14000000 &&
58+
"RawInstr isn't a B or BR immediate instruction");
59+
uint32_t Imm = (static_cast<uint32_t>(Value) & ((1 << 28) - 1)) >> 2;
60+
uint32_t FixedInstr = RawInstr | Imm;
61+
*(little32_t *)FixupPtr = FixedInstr;
62+
break;
63+
}
64+
}
65+
return Error::success();
66+
}
67+
68+
template <uint8_t Bits> static bool fitsRangeSignedInt(int64_t Value) {
69+
return Value >= -(1 << Bits) && Value < (1 << Bits);
70+
}
71+
};
72+
73+
template <typename ELFT>
74+
class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
75+
private:
76+
static Expected<aarch64::EdgeKind_aarch64>
77+
getRelocationKind(const uint32_t Type) {
78+
using namespace aarch64;
79+
switch (Type) {
80+
case ELF::R_AARCH64_CALL26:
81+
return EdgeKind_aarch64::R_AARCH64_CALL26;
82+
}
83+
84+
return make_error<JITLinkError>("Unsupported aarch64 relocation:" +
85+
formatv("{0:d}", Type));
86+
}
87+
88+
Error addRelocations() override {
89+
using Base = ELFLinkGraphBuilder<ELFT>;
90+
LLVM_DEBUG(dbgs() << "Adding relocations\n");
91+
92+
// Iterate sections and only process the interesting ones.
93+
for (auto &SecRef : Base::Sections) {
94+
if (SecRef.sh_type != ELF::SHT_RELA && SecRef.sh_type != ELF::SHT_REL)
95+
continue;
96+
auto RelSectName = Base::Obj.getSectionName(SecRef);
97+
if (!RelSectName)
98+
return RelSectName.takeError();
99+
100+
LLVM_DEBUG({
101+
dbgs() << "Adding relocations from section " << *RelSectName << "\n";
102+
});
103+
104+
auto UpdateSect = Base::Obj.getSection(SecRef.sh_info);
105+
if (!UpdateSect)
106+
return UpdateSect.takeError();
107+
108+
auto UpdateSectName = Base::Obj.getSectionName(**UpdateSect);
109+
if (!UpdateSectName)
110+
return UpdateSectName.takeError();
111+
112+
// Don't process relocations for debug sections.
113+
if (Base::isDwarfSection(*UpdateSectName)) {
114+
LLVM_DEBUG({
115+
dbgs() << " Target is dwarf section " << *UpdateSectName
116+
<< ". Skipping.\n";
117+
});
118+
continue;
119+
}
120+
LLVM_DEBUG(dbgs() << " For target section " << *UpdateSectName << "\n");
121+
122+
auto *JITSection = Base::G->findSectionByName(*UpdateSectName);
123+
if (!JITSection)
124+
return make_error<llvm::StringError>(
125+
"Refencing a section that wasn't added to graph" + *UpdateSectName,
126+
llvm::inconvertibleErrorCode());
127+
128+
auto Relocations = Base::Obj.relas(SecRef);
129+
if (!Relocations)
130+
return Relocations.takeError();
131+
132+
for (const auto &Rela : *Relocations) {
133+
auto Type = Rela.getType(false);
134+
135+
LLVM_DEBUG({
136+
dbgs() << "Relocation Type: " << Type << "\n"
137+
<< "Name: " << Base::Obj.getRelocationTypeName(Type) << "\n";
138+
});
139+
140+
auto SymbolIndex = Rela.getSymbol(false);
141+
auto Symbol = Base::Obj.getRelocationSymbol(Rela, Base::SymTabSec);
142+
if (!Symbol)
143+
return Symbol.takeError();
144+
145+
auto BlockToFix = *(JITSection->blocks().begin());
146+
auto *TargetSymbol = Base::getGraphSymbol(SymbolIndex);
147+
148+
if (!TargetSymbol) {
149+
return make_error<llvm::StringError>(
150+
"Could not find symbol at given index, did you add it to "
151+
"JITSymbolTable? index: " +
152+
std::to_string(SymbolIndex) + ", shndx: " +
153+
std::to_string((*Symbol)->st_shndx) + " Size of table: " +
154+
std::to_string(Base::GraphSymbols.size()),
155+
llvm::inconvertibleErrorCode());
156+
}
157+
int64_t Addend = Rela.r_addend;
158+
JITTargetAddress FixupAddress = (*UpdateSect)->sh_addr + Rela.r_offset;
159+
160+
LLVM_DEBUG({
161+
dbgs() << "Processing relocation at "
162+
<< format("0x%016" PRIx64, FixupAddress) << "\n";
163+
});
164+
auto Kind = getRelocationKind(Type);
165+
if (!Kind)
166+
return Kind.takeError();
167+
168+
BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(),
169+
*TargetSymbol, Addend);
170+
}
171+
}
172+
return Error::success();
173+
}
174+
175+
public:
176+
ELFLinkGraphBuilder_aarch64(StringRef FileName,
177+
const object::ELFFile<ELFT> &Obj, const Triple T)
178+
: ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
179+
aarch64::getEdgeKindName) {}
180+
};
181+
182+
Expected<std::unique_ptr<LinkGraph>>
183+
createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer) {
184+
LLVM_DEBUG({
185+
dbgs() << "Building jitlink graph for new input "
186+
<< ObjectBuffer.getBufferIdentifier() << "...\n";
187+
});
188+
189+
auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
190+
if (!ELFObj)
191+
return ELFObj.takeError();
192+
193+
assert((*ELFObj)->getArch() == Triple::aarch64 &&
194+
"Only AArch64 (little endian) is supported for now");
195+
196+
auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
197+
return ELFLinkGraphBuilder_aarch64<object::ELF64LE>((*ELFObj)->getFileName(),
198+
ELFObjFile.getELFFile(),
199+
(*ELFObj)->makeTriple())
200+
.buildGraph();
201+
}
202+
203+
void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
204+
std::unique_ptr<JITLinkContext> Ctx) {
205+
PassConfiguration Config;
206+
const Triple &TT = G->getTargetTriple();
207+
if (Ctx->shouldAddDefaultTargetPasses(TT)) {
208+
if (auto MarkLive = Ctx->getMarkLivePass(TT))
209+
Config.PrePrunePasses.push_back(std::move(MarkLive));
210+
else
211+
Config.PrePrunePasses.push_back(markAllSymbolsLive);
212+
}
213+
if (auto Err = Ctx->modifyPassConfig(*G, Config))
214+
return Ctx->notifyFailed(std::move(Err));
215+
216+
ELFJITLinker_aarch64::link(std::move(Ctx), std::move(G), std::move(Config));
217+
}
218+
219+
} // namespace jitlink
220+
} // namespace llvm
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===---- aarch64.cpp - Generic JITLink aarch64 edge kinds, utilities -----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Generic utilities for graphs representing aarch64 objects.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/ExecutionEngine/JITLink/aarch64.h"
14+
15+
#define DEBUG_TYPE "jitlink"
16+
17+
namespace llvm {
18+
namespace jitlink {
19+
namespace aarch64 {
20+
21+
const char *getEdgeKindName(Edge::Kind K) {
22+
switch (K) {
23+
case R_AARCH64_CALL26:
24+
return "R_AARCH64_CALL26";
25+
}
26+
return getGenericEdgeKindName(K);
27+
}
28+
} // namespace aarch64
29+
} // namespace jitlink
30+
} // namespace llvm

0 commit comments

Comments
 (0)