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