334 lines · cpp
1//===- bolt/unittest/Passes/InsertNegateRAState.cpp -----------------------===//2//3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9 10#ifdef AARCH64_AVAILABLE11#include "AArch64Subtarget.h"12#include "MCTargetDesc/AArch64MCTargetDesc.h"13#endif // AARCH64_AVAILABLE14 15#include "bolt/Core/BinaryBasicBlock.h"16#include "bolt/Core/BinaryFunction.h"17#include "bolt/Passes/InsertNegateRAStatePass.h"18#include "bolt/Rewrite/BinaryPassManager.h"19#include "bolt/Rewrite/RewriteInstance.h"20#include "llvm/BinaryFormat/ELF.h"21#include "llvm/MC/MCDwarf.h"22#include "llvm/MC/MCInstBuilder.h"23#include "llvm/Support/TargetSelect.h"24#include "gtest/gtest.h"25 26using namespace llvm;27using namespace llvm::object;28using namespace llvm::ELF;29using namespace bolt;30 31namespace {32struct PassTester : public testing::TestWithParam<Triple::ArchType> {33 void SetUp() override {34 initalizeLLVM();35 prepareElf();36 initializeBolt();37 }38 39protected:40 void initalizeLLVM() {41#define BOLT_TARGET(target) \42 LLVMInitialize##target##TargetInfo(); \43 LLVMInitialize##target##TargetMC(); \44 LLVMInitialize##target##AsmParser(); \45 LLVMInitialize##target##Disassembler(); \46 LLVMInitialize##target##Target(); \47 LLVMInitialize##target##AsmPrinter();48 49#include "bolt/Core/TargetConfig.def"50 }51 52#define PREPARE_FUNC(name) \53 constexpr uint64_t FunctionAddress = 0x1000; \54 BinaryFunction *BF = BC->createBinaryFunction( \55 name, *TextSection, FunctionAddress, /*Size=*/0, /*SymbolSize=*/0, \56 /*Alignment=*/16); \57 /* Make sure the pass runs on the BF.*/ \58 BF->updateState(BinaryFunction::State::CFG); \59 BF->setContainedNegateRAState(); \60 /* All tests need at least one BB. */ \61 BinaryBasicBlock *BB = BF->addBasicBlock(); \62 BF->addEntryPoint(*BB); \63 BB->setCFIState(0);64 65 void prepareElf() {66 memcpy(ElfBuf, "\177ELF", 4);67 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);68 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;69 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;70 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;71 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");72 ObjFile = cantFail(ObjectFile::createObjectFile(Source));73 }74 void initializeBolt() {75 Relocation::Arch = ObjFile->makeTriple().getArch();76 BC = cantFail(BinaryContext::createBinaryContext(77 ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),78 ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),79 {llvm::outs(), llvm::errs()}));80 ASSERT_FALSE(!BC);81 BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(82 createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(),83 BC->MRI.get(), BC->STI.get())));84 85 PassManager = std::make_unique<BinaryFunctionPassManager>(*BC);86 PassManager->registerPass(std::make_unique<InsertNegateRAState>());87 88 TextSection = &BC->registerOrUpdateSection(89 ".text", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_EXECINSTR,90 /*Data=*/nullptr, /*Size=*/0,91 /*Alignment=*/16);92 }93 94 std::vector<int> findCFIOffsets(BinaryFunction &BF) {95 std::vector<int> Locations;96 int Idx = 0;97 int InstSize = 4; // AArch6498 for (BinaryBasicBlock &BB : BF) {99 for (MCInst &Inst : BB) {100 if (BC->MIB->isCFI(Inst)) {101 const MCCFIInstruction *CFI = BF.getCFIFor(Inst);102 if (CFI->getOperation() == MCCFIInstruction::OpNegateRAState)103 Locations.push_back(Idx * InstSize);104 }105 Idx++;106 }107 }108 return Locations;109 }110 111 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};112 std::unique_ptr<ObjectFile> ObjFile;113 std::unique_ptr<BinaryContext> BC;114 std::unique_ptr<BinaryFunctionPassManager> PassManager;115 BinarySection *TextSection;116};117} // namespace118 119TEST_P(PassTester, ExampleTest) {120 if (GetParam() != Triple::aarch64)121 GTEST_SKIP();122 123 ASSERT_NE(TextSection, nullptr);124 125 PREPARE_FUNC("ExampleFunction");126 127 MCInst UnsignedInst = MCInstBuilder(AArch64::ADDSXri)128 .addReg(AArch64::X0)129 .addReg(AArch64::X0)130 .addImm(0)131 .addImm(0);132 BC->MIB->setRAState(UnsignedInst, false);133 BB->addInstruction(UnsignedInst);134 135 MCInst SignedInst = MCInstBuilder(AArch64::ADDSXri)136 .addReg(AArch64::X0)137 .addReg(AArch64::X0)138 .addImm(1)139 .addImm(0);140 BC->MIB->setRAState(SignedInst, true);141 BB->addInstruction(SignedInst);142 143 Error E = PassManager->runPasses();144 EXPECT_FALSE(E);145 146 /* Expected layout of BF after the pass:147 148 .LBB0 (3 instructions, align : 1)149 Entry Point150 CFI State : 0151 00000000: adds x0, x0, #0x0152 00000004: !CFI $0 ; OpNegateRAState153 00000004: adds x0, x0, #0x1154 CFI State: 0155 */156 auto CFILoc = findCFIOffsets(*BF);157 EXPECT_EQ(CFILoc.size(), 1u);158 EXPECT_EQ(CFILoc[0], 4);159}160 161TEST_P(PassTester, fillUnknownStateInBBTest) {162 /* Check that a if BB starts with unknown RAState, we can fill the unknown163 states based on following instructions with known RAStates.164 *165 * .LBB0 (1 instructions, align : 1)166 Entry Point167 CFI State : 0168 00000000: adds x0, x0, #0x0169 CFI State: 0170 171 .LBB1 (4 instructions, align : 1)172 CFI State : 0173 00000004: !CFI $0 ; OpNegateRAState174 00000004: adds x0, x0, #0x1175 00000008: adds x0, x0, #0x2176 0000000c: adds x0, x0, #0x3177 CFI State: 0178 */179 if (GetParam() != Triple::aarch64)180 GTEST_SKIP();181 182 ASSERT_NE(TextSection, nullptr);183 184 PREPARE_FUNC("FuncWithUnknownStateInBB");185 BinaryBasicBlock *BB2 = BF->addBasicBlock();186 BB2->setCFIState(0);187 188 MCInst Unsigned = MCInstBuilder(AArch64::ADDSXri)189 .addReg(AArch64::X0)190 .addReg(AArch64::X0)191 .addImm(0)192 .addImm(0);193 BC->MIB->setRAState(Unsigned, false);194 BB->addInstruction(Unsigned);195 196 MCInst Unknown = MCInstBuilder(AArch64::ADDSXri)197 .addReg(AArch64::X0)198 .addReg(AArch64::X0)199 .addImm(1)200 .addImm(0);201 MCInst Unknown1 = MCInstBuilder(AArch64::ADDSXri)202 .addReg(AArch64::X0)203 .addReg(AArch64::X0)204 .addImm(2)205 .addImm(0);206 MCInst Signed = MCInstBuilder(AArch64::ADDSXri)207 .addReg(AArch64::X0)208 .addReg(AArch64::X0)209 .addImm(3)210 .addImm(0);211 BC->MIB->setRAState(Signed, true);212 BB2->addInstruction(Unknown);213 BB2->addInstruction(Unknown1);214 BB2->addInstruction(Signed);215 216 Error E = PassManager->runPasses();217 EXPECT_FALSE(E);218 219 auto CFILoc = findCFIOffsets(*BF);220 EXPECT_EQ(CFILoc.size(), 1u);221 EXPECT_EQ(CFILoc[0], 4);222 // Check that the pass set Unknown and Unknown1 to signed.223 // begin() is the CFI, begin() + 1 is Unknown, begin() + 2 is Unknown1.224 std::optional<bool> RAState = BC->MIB->getRAState(*(BB2->begin() + 1));225 EXPECT_TRUE(RAState.has_value());226 EXPECT_TRUE(*RAState);227 std::optional<bool> RAState1 = BC->MIB->getRAState(*(BB2->begin() + 2));228 EXPECT_TRUE(RAState1.has_value());229 EXPECT_TRUE(*RAState1);230}231 232TEST_P(PassTester, fillUnknownStubs) {233 /*234 * Stubs that are not part of the function's CFG should inherit the RAState of235 the BasicBlock before it.236 *237 * LBB1 is not part of the CFG: LBB0 jumps unconditionally to LBB2.238 * LBB1 would be a stub inserted in LongJmp in real code.239 * We do not add any NegateRAState CFIs, as other CFIs are not added either.240 * See issue #160989 for more details.241 *242 * .LBB0 (1 instructions, align : 1)243 Entry Point244 00000000: b .LBB2245 Successors: .LBB2246 247 .LBB1 (1 instructions, align : 1)248 00000004: ret249 250 .LBB2 (1 instructions, align : 1)251 Predecessors: .LBB0252 00000008: ret253 */254 if (GetParam() != Triple::aarch64)255 GTEST_SKIP();256 257 ASSERT_NE(TextSection, nullptr);258 259 PREPARE_FUNC("FuncWithStub");260 BinaryBasicBlock *BB2 = BF->addBasicBlock();261 BB2->setCFIState(0);262 BinaryBasicBlock *BB3 = BF->addBasicBlock();263 BB3->setCFIState(0);264 265 BB->addSuccessor(BB3);266 267 // Jumping over BB2, to BB3.268 MCInst Jump;269 BC->MIB->createUncondBranch(Jump, BB3->getLabel(), BC->Ctx.get());270 BB->addInstruction(Jump);271 BC->MIB->setRAState(Jump, false);272 273 // BB2, in real code it would be a ShortJmp.274 // Unknown RAState.275 MCInst StubInst;276 BC->MIB->createReturn(StubInst);277 BB2->addInstruction(StubInst);278 279 // Can be any instruction.280 MCInst Ret;281 BC->MIB->createReturn(Ret);282 BB3->addInstruction(Ret);283 BC->MIB->setRAState(Ret, false);284 285 Error E = PassManager->runPasses();286 EXPECT_FALSE(E);287 288 // Check that we did not generate any NegateRAState CFIs.289 auto CFILoc = findCFIOffsets(*BF);290 EXPECT_EQ(CFILoc.size(), 0u);291}292 293TEST_P(PassTester, fillUnknownStubsEmpty) {294 /*295 * This test checks that BOLT can set the RAState of unknown BBs,296 * even if all previous BBs are empty, hence no PrevInst gets set.297 *298 * As this means that the current (empty) BB is the first with non-pseudo299 * instructions, the function's initialRAState should be used.300 */301 if (GetParam() != Triple::aarch64)302 GTEST_SKIP();303 304 ASSERT_NE(TextSection, nullptr);305 306 PREPARE_FUNC("FuncWithStub");307 BF->setInitialRAState(false);308 BinaryBasicBlock *BB2 = BF->addBasicBlock();309 BB2->setCFIState(0);310 311 // BB is empty.312 BB->addSuccessor(BB2);313 314 // BB2, in real code it would be a ShortJmp.315 // Unknown RAState.316 MCInst StubInst;317 BC->MIB->createReturn(StubInst);318 BB2->addInstruction(StubInst);319 320 Error E = PassManager->runPasses();321 EXPECT_FALSE(E);322 323 // Check that BOLT added an RAState to BB2.324 std::optional<bool> RAState = BC->MIB->getRAState(*(BB2->begin()));325 EXPECT_TRUE(RAState.has_value());326 // BB2 should be set to BF.initialRAState (false).327 EXPECT_FALSE(*RAState);328}329 330#ifdef AARCH64_AVAILABLE331INSTANTIATE_TEST_SUITE_P(AArch64, PassTester,332 ::testing::Values(Triple::aarch64));333#endif334