xref: /llvm-project/llvm/unittests/ExecutionEngine/JITLink/AArch32ErrorTests.cpp (revision 4eaff6c58ae2f130ac8d63cf2c87bbb483114876)
1 //===------- AArch32ErrorTests.cpp - Test AArch32 error handling ----------===//
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 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
10 #include "llvm/TargetParser/Triple.h"
11 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest.h"
13 #include <llvm/ExecutionEngine/JITLink/aarch32.h>
14 
15 using namespace llvm;
16 using namespace llvm::jitlink;
17 using namespace llvm::jitlink::aarch32;
18 using namespace llvm::support;
19 using namespace llvm::support::endian;
20 
21 auto G = std::make_unique<LinkGraph>(
22     "foo", std::make_shared<orc::SymbolStringPool>(),
23     Triple("armv7-linux-gnueabi"), SubtargetFeatures(),
24     aarch32::getEdgeKindName);
25 auto &Sec =
26     G->createSection("__data", orc::MemProt::Read | orc::MemProt::Write);
27 
28 auto ArmCfg = getArmConfigForCPUArch(ARMBuildAttrs::v7);
29 
30 constexpr uint64_t ArmAlignment = 4;
31 constexpr uint64_t ThumbAlignment = 2;
32 constexpr uint64_t AlignmentOffset = 0;
33 
34 constexpr orc::ExecutorAddrDiff SymbolOffset = 0;
35 constexpr orc::ExecutorAddrDiff SymbolSize = 4;
36 
37 class AArch32Errors : public testing::Test {
38 protected:
39   const ArmConfig Cfg = getArmConfigForCPUArch(ARMBuildAttrs::v7);
40   std::unique_ptr<LinkGraph> G;
41   Section *S = nullptr;
42 
43   const uint8_t Zeros[4]{0x00, 0x00, 0x00, 0x00};
44   uint8_t MutableZeros[4]{0x00, 0x00, 0x00, 0x00};
45 
46 public:
47   static void SetUpTestCase() {}
48 
49   void SetUp() override {
50     G = std::make_unique<LinkGraph>(
51         "foo", std::make_shared<orc::SymbolStringPool>(),
52         Triple("armv7-linux-gnueabi"), SubtargetFeatures(),
53         aarch32::getEdgeKindName);
54     S = &G->createSection("__data", orc::MemProt::Read | orc::MemProt::Write);
55   }
56 
57   void TearDown() override {}
58 
59 protected:
60   template <size_t Size>
61   Block &createBlock(const uint8_t (&Content)[Size], uint64_t Addr,
62                      uint64_t Alignment = 4) {
63     ArrayRef<char> CharContent{reinterpret_cast<const char *>(&Content),
64                                sizeof(Content)};
65     return G->createContentBlock(*S, CharContent, orc::ExecutorAddr(Addr),
66                                  Alignment, AlignmentOffset);
67   }
68 
69   template <size_t Size>
70   Block &createMutableBlock(uint8_t (&Content)[Size], uint64_t Addr,
71                             uint64_t Alignment = 4) {
72     MutableArrayRef<char> CharContent{reinterpret_cast<char *>(&Content),
73                                       sizeof(Content)};
74     return G->createMutableContentBlock(
75         *S, CharContent, orc::ExecutorAddr(Addr), Alignment, AlignmentOffset);
76   }
77 
78   Symbol &createSymbolWithDistance(Block &Origin, uint64_t Dist) {
79     uint64_t TargetAddr = Origin.getAddress().getValue() + Dist;
80     return G->addAnonymousSymbol(createBlock(Zeros, TargetAddr), 0 /*Offset*/,
81                                  G->getPointerSize(), false, false);
82   };
83 
84   template <endianness Endian> void write(uint8_t *Mem, HalfWords Data) {
85     write16<Endian>(Mem, Data.Hi);
86     write16<Endian>(Mem + 2, Data.Lo);
87   }
88 };
89 
90 TEST_F(AArch32Errors, readAddendDataGeneric) {
91   Block &ZerosBlock = createBlock(Zeros, 0x1000);
92   constexpr uint64_t ZerosOffset = 0;
93 
94   // Invalid edge kind is the only error we can raise here right now.
95   Edge::Kind Invalid = Edge::GenericEdgeKind::Invalid;
96   EXPECT_THAT_EXPECTED(readAddend(*G, ZerosBlock, ZerosOffset, Invalid, Cfg),
97                        FailedWithMessage(testing::HasSubstr(
98                            "can not read implicit addend for aarch32 edge kind "
99                            "INVALID RELOCATION")));
100 }
101 
102 TEST(AArch32_ELF, readAddendArmErrors) {
103 
104   constexpr orc::ExecutorAddr B1DummyAddr(0x1000);
105 
106   // Permanently undefined instruction in ARM
107   //    udf #0
108   uint8_t ArmWord[] = {0xf0, 0x00, 0xf0, 0xe7};
109   ArrayRef<char> ArmContent(reinterpret_cast<const char *>(&ArmWord),
110                             sizeof(ArmWord));
111   auto &BArm = G->createContentBlock(Sec, ArmContent, B1DummyAddr, ArmAlignment,
112                                      AlignmentOffset);
113 
114   for (Edge::Kind K = FirstArmRelocation; K < LastArmRelocation; K += 1) {
115     EXPECT_THAT_EXPECTED(readAddend(*G, BArm, SymbolOffset, K, ArmCfg),
116                          FailedWithMessage(testing::AllOf(
117                              testing::StartsWith("Invalid opcode"),
118                              testing::EndsWith(aarch32::getEdgeKindName(K)))));
119   }
120 }
121 
122 TEST(AArch32_ELF, readAddendThumbErrors) {
123 
124   constexpr orc::ExecutorAddr B2DummyAddr(0x2000);
125 
126   // Permanently undefined instruction in Thumb
127   //    udf #0
128   //
129   //    11110:op:imm4:1:op1:imm12
130   //    op  = 1111111 Permanent undefined
131   //    op1 = 010
132   //
133   constexpr HalfWords ThumbHalfWords{0xf7f0, 0xa000};
134   ArrayRef<char> ThumbContent(reinterpret_cast<const char *>(&ThumbHalfWords),
135                               sizeof(ThumbHalfWords));
136   auto &BThumb = G->createContentBlock(Sec, ThumbContent, B2DummyAddr,
137                                        ThumbAlignment, AlignmentOffset);
138 
139   for (Edge::Kind K = FirstThumbRelocation; K < LastThumbRelocation; K += 1) {
140     EXPECT_THAT_EXPECTED(readAddend(*G, BThumb, SymbolOffset, K, ArmCfg),
141                          FailedWithMessage(testing::AllOf(
142                              testing::StartsWith("Invalid opcode"),
143                              testing::EndsWith(aarch32::getEdgeKindName(K)))));
144   }
145 }
146 
147 TEST_F(AArch32Errors, applyFixupDataGeneric) {
148   Block &OriginBlock = createMutableBlock(MutableZeros, 0x1000);
149   Block &TargetBlock = createBlock(Zeros, 0x2000);
150 
151   constexpr uint64_t OffsetInTarget = 0;
152   Symbol &TargetSymbol = G->addAnonymousSymbol(
153       TargetBlock, OffsetInTarget, G->getPointerSize(), false, false);
154 
155   constexpr uint64_t OffsetInOrigin = 0;
156   Edge::Kind Invalid = Edge::GenericEdgeKind::Invalid;
157   Edge InvalidEdge(Invalid, OffsetInOrigin, TargetSymbol, 0 /*Addend*/);
158   EXPECT_THAT_ERROR(
159       applyFixup(*G, OriginBlock, InvalidEdge, Cfg),
160       FailedWithMessage(testing::HasSubstr(
161           "encountered unfixable aarch32 edge kind INVALID RELOCATION")));
162 }
163 
164 TEST(AArch32_ELF, applyFixupArmErrors) {
165 
166   constexpr orc::ExecutorAddr B3DummyAddr(0x5000);
167 
168   uint8_t ArmWord[] = {0xf0, 0x00, 0xf0, 0xe7};
169   MutableArrayRef<char> MutableArmContent(reinterpret_cast<char *>(ArmWord),
170                                           sizeof(ArmWord));
171 
172   auto &BArm = G->createMutableContentBlock(Sec, MutableArmContent, B3DummyAddr,
173                                             ArmAlignment, AlignmentOffset);
174 
175   Symbol &TargetSymbol =
176       G->addAnonymousSymbol(BArm, SymbolOffset, SymbolSize, false, false);
177 
178   for (Edge::Kind K = FirstArmRelocation; K < LastArmRelocation; K += 1) {
179     Edge E(K, 0, TargetSymbol, 0);
180     EXPECT_THAT_ERROR(applyFixup(*G, BArm, E, ArmCfg),
181                       FailedWithMessage(testing::AllOf(
182                           testing::StartsWith("Invalid opcode"),
183                           testing::EndsWith(aarch32::getEdgeKindName(K)))));
184   }
185 }
186 
187 TEST(AArch32_ELF, applyFixupThumbErrors) {
188 
189   struct MutableHalfWords {
190     constexpr MutableHalfWords(HalfWords Preset)
191         : Hi(Preset.Hi), Lo(Preset.Lo) {}
192 
193     uint16_t Hi; // First halfword
194     uint16_t Lo; // Second halfword
195   };
196 
197   constexpr orc::ExecutorAddr B4DummyAddr(0x6000);
198 
199   // Permanently undefined instruction in Thumb
200   //    udf #0
201   //
202   //    11110:op:imm4:1:op1:imm12
203   //    op  = 1111111 Permanent undefined
204   //    op1 = 010
205   //
206   constexpr HalfWords ThumbHalfWords{0xf7f0, 0xa000};
207   MutableHalfWords MutableThumbHalfWords{ThumbHalfWords};
208   MutableArrayRef<char> MutableThumbContent(
209       reinterpret_cast<char *>(&MutableThumbHalfWords),
210       sizeof(MutableThumbHalfWords));
211 
212   auto &BThumb = G->createMutableContentBlock(
213       Sec, MutableThumbContent, B4DummyAddr, ThumbAlignment, AlignmentOffset);
214   Symbol &TargetSymbol =
215       G->addAnonymousSymbol(BThumb, SymbolOffset, SymbolSize, false, false);
216 
217   for (Edge::Kind K = FirstThumbRelocation; K < LastThumbRelocation; K += 1) {
218     Edge E(K, 0, TargetSymbol, 0);
219     EXPECT_THAT_ERROR(applyFixup(*G, BThumb, E, ArmCfg),
220                       FailedWithMessage(testing::AllOf(
221                           testing::StartsWith("Invalid opcode"),
222                           testing::EndsWith(aarch32::getEdgeKindName(K)))));
223   }
224 }
225 
226 TEST_F(AArch32Errors, applyFixupThumbCall) {
227   // Check range of R_ARM_THM_CALL relocation
228   constexpr uint64_t Call1Offset = 0; //< first out-of-range
229   constexpr uint64_t Call2Offset = 4; //< last in-range
230 
231   uint8_t TwoCallsMem[8];
232   Block &Site = createMutableBlock(TwoCallsMem, 0);
233   constexpr HalfWords CallOpcode = FixupInfo<Thumb_Call>::Opcode;
234   write<endianness::little>(TwoCallsMem + Call1Offset, CallOpcode);
235   write<endianness::little>(TwoCallsMem + Call2Offset, CallOpcode);
236 
237   // Thumb call with J1J2-encoding has range of 25 bit
238   ArmConfig ArmCfg;
239   ArmCfg.J1J2BranchEncoding = true;
240   Symbol &J1J2Target = createSymbolWithDistance(Site, 0x01ull << 24);
241   {
242     Edge LastInRange(Thumb_Call, Call2Offset, J1J2Target, 0);
243     EXPECT_THAT_ERROR(applyFixup(*G, Site, LastInRange, ArmCfg), Succeeded());
244     Edge FirstOutOfRange(Thumb_Call, Call1Offset, J1J2Target, 0);
245     EXPECT_THAT_ERROR(applyFixup(*G, Site, FirstOutOfRange, ArmCfg),
246                       FailedWithMessage(testing::HasSubstr("out of range")));
247   }
248 
249   // Thumb call without J1J2-encoding has range of 22 bit
250   ArmCfg.J1J2BranchEncoding = false;
251   Symbol &NonJ1J2Target = createSymbolWithDistance(Site, 0x01ull << 21);
252   {
253     Edge LastInRange(Thumb_Call, Call2Offset, NonJ1J2Target, 0);
254     EXPECT_THAT_ERROR(applyFixup(*G, Site, LastInRange, ArmCfg), Succeeded());
255     Edge FirstOutOfRange(Thumb_Call, Call1Offset, NonJ1J2Target, 0);
256     EXPECT_THAT_ERROR(applyFixup(*G, Site, FirstOutOfRange, ArmCfg),
257                       FailedWithMessage(testing::HasSubstr("out of range")));
258   }
259 }
260 
261 TEST_F(AArch32Errors, applyFixupThumbJump24) {
262   // Check range of R_ARM_THM_JUMP24 relocation
263   constexpr uint64_t Jump1Offset = 0; //< first out-of-range
264   constexpr uint64_t Jump2Offset = 4; //< last in-range
265 
266   uint8_t TwoJumpsMem[8];
267   constexpr HalfWords JumpOpcode = FixupInfo<Thumb_Jump24>::Opcode;
268   write<endianness::little>(TwoJumpsMem + Jump1Offset, JumpOpcode);
269   write<endianness::little>(TwoJumpsMem + Jump2Offset, JumpOpcode);
270   Block &Site = createMutableBlock(TwoJumpsMem, 0);
271 
272   // Thumb Jump24 with J1J2-encoding has range of 25 bit
273   ArmCfg.J1J2BranchEncoding = true;
274   Symbol &J1J2Target = createSymbolWithDistance(Site, 0x01ull << 24);
275   J1J2Target.setTargetFlags(TargetFlags_aarch32::ThumbSymbol);
276   {
277     Edge LastInRange(Thumb_Jump24, Jump2Offset, J1J2Target, 0);
278     EXPECT_THAT_ERROR(applyFixup(*G, Site, LastInRange, ArmCfg), Succeeded());
279     Edge FirstOutOfRange(Thumb_Jump24, Jump1Offset, J1J2Target, 0);
280     EXPECT_THAT_ERROR(applyFixup(*G, Site, FirstOutOfRange, ArmCfg),
281                       FailedWithMessage(testing::HasSubstr("out of range")));
282   }
283 
284   // Thumb Jump24 without J1J2-encoding has range of 22 bit
285   ArmCfg.J1J2BranchEncoding = false;
286   Symbol &NonJ1J2Target = createSymbolWithDistance(Site, 0x01ull << 21);
287   NonJ1J2Target.setTargetFlags(TargetFlags_aarch32::ThumbSymbol);
288   {
289     Edge LastInRange(Thumb_Jump24, Jump2Offset, NonJ1J2Target, 0);
290     EXPECT_THAT_ERROR(applyFixup(*G, Site, LastInRange, ArmCfg), Succeeded());
291     Edge FirstOutOfRange(Thumb_Jump24, Jump1Offset, NonJ1J2Target, 0);
292     EXPECT_THAT_ERROR(applyFixup(*G, Site, FirstOutOfRange, ArmCfg),
293                       FailedWithMessage(testing::HasSubstr("out of range")));
294   }
295 
296   // Check that branching to an ARM target with Jump24 fails
297   Symbol &ArmTarget = createSymbolWithDistance(Site, 0x1000);
298   assert((ArmTarget.getTargetFlags() & TargetFlags_aarch32::ThumbSymbol) == 0);
299   Edge Interworking(Thumb_Jump24, Jump2Offset, ArmTarget, 0);
300   EXPECT_THAT_ERROR(applyFixup(*G, Site, Interworking, ArmCfg),
301                     FailedWithMessage(testing::HasSubstr(
302                         "Branch relocation needs interworking "
303                         "stub when bridging to ARM")));
304 }
305