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 bool isLoadStoreImm12(uint32_t Instr) {
22   constexpr uint32_t LoadStoreImm12Mask = 0x3b000000;
23   return (Instr & LoadStoreImm12Mask) == 0x39000000;
24 }
25 
26 unsigned getPageOffset12Shift(uint32_t Instr) {
27   constexpr uint32_t Vec128Mask = 0x04800000;
28 
29   if (isLoadStoreImm12(Instr)) {
30     uint32_t ImplicitShift = Instr >> 30;
31     if (ImplicitShift == 0)
32       if ((Instr & Vec128Mask) == Vec128Mask)
33         ImplicitShift = 4;
34 
35     return ImplicitShift;
36   }
37 
38   return 0;
39 }
40 
41 Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
42   using namespace support;
43 
44   char *BlockWorkingMem = B.getAlreadyMutableContent().data();
45   char *FixupPtr = BlockWorkingMem + E.getOffset();
46   orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
47 
48   switch (E.getKind()) {
49   case Branch26: {
50     assert((FixupAddress.getValue() & 0x3) == 0 &&
51            "Branch-inst is not 32-bit aligned");
52 
53     int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
54 
55     if (static_cast<uint64_t>(Value) & 0x3)
56       return make_error<JITLinkError>("Branch26 target is not 32-bit "
57                                       "aligned");
58 
59     if (Value < -(1 << 27) || Value > ((1 << 27) - 1))
60       return makeTargetOutOfRangeError(G, B, E);
61 
62     uint32_t RawInstr = *(little32_t *)FixupPtr;
63     assert((RawInstr & 0x7fffffff) == 0x14000000 &&
64            "RawInstr isn't a B or BR immediate instruction");
65     uint32_t Imm = (static_cast<uint32_t>(Value) & ((1 << 28) - 1)) >> 2;
66     uint32_t FixedInstr = RawInstr | Imm;
67     *(little32_t *)FixupPtr = FixedInstr;
68     break;
69   }
70   case Pointer32: {
71     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
72     if (Value > std::numeric_limits<uint32_t>::max())
73       return makeTargetOutOfRangeError(G, B, E);
74     *(ulittle32_t *)FixupPtr = Value;
75     break;
76   }
77   case Pointer64:
78   case Pointer64Anon: {
79     uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
80     *(ulittle64_t *)FixupPtr = Value;
81     break;
82   }
83   case Page21:
84   case TLVPage21:
85   case GOTPage21: {
86     assert((E.getKind() != GOTPage21 || E.getAddend() == 0) &&
87            "GOTPAGE21 with non-zero addend");
88     uint64_t TargetPage =
89         (E.getTarget().getAddress().getValue() + E.getAddend()) &
90         ~static_cast<uint64_t>(4096 - 1);
91     uint64_t PCPage =
92         FixupAddress.getValue() & ~static_cast<uint64_t>(4096 - 1);
93 
94     int64_t PageDelta = TargetPage - PCPage;
95     if (!isInt<33>(PageDelta))
96       return makeTargetOutOfRangeError(G, B, E);
97 
98     uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
99     assert((RawInstr & 0xffffffe0) == 0x90000000 &&
100            "RawInstr isn't an ADRP instruction");
101     uint32_t ImmLo = (static_cast<uint64_t>(PageDelta) >> 12) & 0x3;
102     uint32_t ImmHi = (static_cast<uint64_t>(PageDelta) >> 14) & 0x7ffff;
103     uint32_t FixedInstr = RawInstr | (ImmLo << 29) | (ImmHi << 5);
104     *(ulittle32_t *)FixupPtr = FixedInstr;
105     break;
106   }
107   case PageOffset12: {
108     uint64_t TargetOffset =
109         (E.getTarget().getAddress() + E.getAddend()).getValue() & 0xfff;
110 
111     uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
112     unsigned ImmShift = getPageOffset12Shift(RawInstr);
113 
114     if (TargetOffset & ((1 << ImmShift) - 1))
115       return make_error<JITLinkError>("PAGEOFF12 target is not aligned");
116 
117     uint32_t EncodedImm = (TargetOffset >> ImmShift) << 10;
118     uint32_t FixedInstr = RawInstr | EncodedImm;
119     *(ulittle32_t *)FixupPtr = FixedInstr;
120     break;
121   }
122   case TLVPageOffset12:
123   case GOTPageOffset12: {
124     assert(E.getAddend() == 0 && "GOTPAGEOF12 with non-zero addend");
125 
126     uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
127     assert((RawInstr & 0xfffffc00) == 0xf9400000 &&
128            "RawInstr isn't a 64-bit LDR immediate");
129 
130     uint32_t TargetOffset = E.getTarget().getAddress().getValue() & 0xfff;
131     assert((TargetOffset & 0x7) == 0 && "GOT entry is not 8-byte aligned");
132     uint32_t EncodedImm = (TargetOffset >> 3) << 10;
133     uint32_t FixedInstr = RawInstr | EncodedImm;
134     *(ulittle32_t *)FixupPtr = FixedInstr;
135     break;
136   }
137   case LDRLiteral19: {
138     assert((FixupAddress.getValue() & 0x3) == 0 && "LDR is not 32-bit aligned");
139     assert(E.getAddend() == 0 && "LDRLiteral19 with non-zero addend");
140     uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
141     assert(RawInstr == 0x58000010 && "RawInstr isn't a 64-bit LDR literal");
142     int64_t Delta = E.getTarget().getAddress() - FixupAddress;
143     if (Delta & 0x3)
144       return make_error<JITLinkError>("LDR literal target is not 32-bit "
145                                       "aligned");
146     if (Delta < -(1 << 20) || Delta > ((1 << 20) - 1))
147       return makeTargetOutOfRangeError(G, B, E);
148 
149     uint32_t EncodedImm = ((static_cast<uint32_t>(Delta) >> 2) & 0x7ffff) << 5;
150     uint32_t FixedInstr = RawInstr | EncodedImm;
151     *(ulittle32_t *)FixupPtr = FixedInstr;
152     break;
153   }
154   case Delta32:
155   case Delta64:
156   case NegDelta32:
157   case NegDelta64: {
158     int64_t Value;
159     if (E.getKind() == Delta32 || E.getKind() == Delta64)
160       Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
161     else
162       Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
163 
164     if (E.getKind() == Delta32 || E.getKind() == NegDelta32) {
165       if (Value < std::numeric_limits<int32_t>::min() ||
166           Value > std::numeric_limits<int32_t>::max())
167         return makeTargetOutOfRangeError(G, B, E);
168       *(little32_t *)FixupPtr = Value;
169     } else
170       *(little64_t *)FixupPtr = Value;
171     break;
172   }
173   default:
174     return make_error<JITLinkError>(
175         "In graph " + G.getName() + ", section " + B.getSection().getName() +
176         "unsupported edge kind" + getEdgeKindName(E.getKind()));
177   }
178 
179   return Error::success();
180 }
181 
182 const char *getEdgeKindName(Edge::Kind R) {
183   switch (R) {
184   case Branch26:
185     return "Branch26";
186   case Pointer64:
187     return "Pointer64";
188   case Pointer64Anon:
189     return "Pointer64Anon";
190   case Page21:
191     return "Page21";
192   case PageOffset12:
193     return "PageOffset12";
194   case GOTPage21:
195     return "GOTPage21";
196   case GOTPageOffset12:
197     return "GOTPageOffset12";
198   case TLVPage21:
199     return "TLVPage21";
200   case TLVPageOffset12:
201     return "TLVPageOffset12";
202   case PointerToGOT:
203     return "PointerToGOT";
204   case PairedAddend:
205     return "PairedAddend";
206   case LDRLiteral19:
207     return "LDRLiteral19";
208   case Delta32:
209     return "Delta32";
210   case Delta64:
211     return "Delta64";
212   case NegDelta32:
213     return "NegDelta32";
214   case NegDelta64:
215     return "NegDelta64";
216   default:
217     return getGenericEdgeKindName(static_cast<Edge::Kind>(R));
218   }
219 }
220 
221 } // namespace aarch64
222 } // namespace jitlink
223 } // namespace llvm
224