brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.0 KiB · 2ef78d3 Raw
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