[PR] Fix LongJmp pass

Summary:
This patch handles 2 problems with LongJmp pass:
1. The pass should be executed before FinalizeFunctions, since the pass
may add new entry points for the function, and the
BinaryFunction::addEntryPoint has an assert "CurrentState == State::CFG"
2. Replaced shortJmp implementation with position-independent code.
Currently we could handle PIC binaries with max +-4Gb offsets, the
longJmp uses absolute addreses and could could be used only in non-PIE
binaries.

Vladislav Khmelevsky,
Advanced Software Technology Lab, Huawei

(cherry picked from FBD31416925)
This commit is contained in:
Vladislav Khmelevsky 2021-10-04 19:17:01 +03:00 committed by Maksim Panchenko
parent 96bb090653
commit a2214e8f0d
7 changed files with 62 additions and 50 deletions

View File

@ -510,6 +510,15 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
// memory profiling data.
Manager.registerPass(std::make_unique<ReorderData>());
if (BC.isAArch64()) {
Manager.registerPass(std::make_unique<ADRRelaxationPass>());
// Tighten branches according to offset differences between branch and
// targets. No extra instructions after this pass, otherwise we may have
// relocations out of range and crash during linking.
Manager.registerPass(std::make_unique<LongJmpPass>(PrintLongJmp));
}
// This pass should always run last.*
Manager.registerPass(std::make_unique<FinalizeFunctions>(PrintFinalized));
@ -531,15 +540,6 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
if (BC.HasRelocations)
Manager.registerPass(std::make_unique<PatchEntries>());
if (BC.isAArch64()) {
Manager.registerPass(std::make_unique<ADRRelaxationPass>());
// Tighten branches according to offset differences between branch and
// targets. No extra instructions after this pass, otherwise we may have
// relocations out of range and crash during linking.
Manager.registerPass(std::make_unique<LongJmpPass>(PrintLongJmp));
}
// This pass turns tail calls into jumps which makes them invisible to
// function reordering. It's unsafe to use any CFG or instruction analysis
// after this point.

View File

@ -565,7 +565,7 @@ public:
void addLineTableSequence(const DWARFDebugLine::LineTable *Table,
uint32_t FirstRow, uint32_t LastRow,
uint64_t EndOfSequenceAddress) {
assert(!InputTable || InputTable == Table && "expected same table for CU");
assert((!InputTable || InputTable == Table) && "expected same table for CU");
InputTable = Table;
InputSequences.emplace_back(

View File

@ -1356,12 +1356,12 @@ public:
}
virtual void createLongJmp(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) const {
MCContext *Ctx, bool IsTailCall = false) {
llvm_unreachable("not implemented");
}
virtual void createShortJmp(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) const {
MCContext *Ctx, bool IsTailCall = false) {
llvm_unreachable("not implemented");
}
@ -1451,6 +1451,11 @@ public:
return false;
}
virtual void createLongTailCall(std::vector<MCInst> &Seq,
const MCSymbol *Target, MCContext *Ctx) {
llvm_unreachable("not implemented");
}
/// Creates a trap instruction in Inst.
///
/// Returns true on success.

View File

@ -475,9 +475,12 @@ bool LongJmpPass::relaxStub(BinaryBasicBlock &StubBB) {
return true;
}
// Needs a long jmp
if (Bits > RangeShortJmp)
return false;
// The long jmp uses absolute address on AArch64
// So we could not use it for PIC binaries
if (BC.isAArch64() && !BC.HasFixedLoadAddress) {
errs() << "BOLT-ERROR: Unable to relax stub for PIC binary\n";
exit(1);
}
LLVM_DEBUG(dbgs() << "Relaxing stub to long jump. PCRelTgtAddress = "
<< Twine::utohexstr(PCRelTgtAddress)
@ -577,6 +580,8 @@ bool LongJmpPass::relax(BinaryFunction &Func) {
InsertionPoint == Frontier
? FrontierAddress
: DotAddress));
DotAddress += InsnSize;
}
}

View File

@ -57,10 +57,9 @@ void PatchEntries::runOnFunctions(BinaryContext &BC) {
// Calculate the size of the patch.
static size_t PatchSize = 0;
if (!PatchSize) {
MCInst TailCallInst;
BC.MIB->createTailCall(TailCallInst, BC.Ctx->createTempSymbol(),
BC.Ctx.get());
PatchSize = BC.computeInstructionSize(TailCallInst);
std::vector<MCInst> Seq;
BC.MIB->createLongTailCall(Seq, BC.Ctx->createTempSymbol(), BC.Ctx.get());
PatchSize = BC.computeCodeSize(Seq.begin(), Seq.end());
}
for (auto &BFI : BC.getBinaryFunctions()) {
@ -125,9 +124,9 @@ void PatchEntries::runOnFunctions(BinaryContext &BC) {
PatchFunction->setFileOffset(Patch.FileOffset);
PatchFunction->setOriginSection(Patch.Section);
MCInst TailCallInst;
BC.MIB->createTailCall(TailCallInst, Patch.Symbol, BC.Ctx.get());
PatchFunction->addBasicBlock(0)->addInstruction(TailCallInst);
std::vector<MCInst> Seq;
BC.MIB->createLongTailCall(Seq, Patch.Symbol, BC.Ctx.get());
PatchFunction->addBasicBlock(0)->addInstructions(Seq);
// Verify the size requirements.
uint64_t HotSize, ColdSize;

View File

@ -800,7 +800,7 @@ public:
}
int getShortJmpEncodingSize() const override {
return 32;
return 33;
}
int getUncondBranchEncodingSize() const override {
@ -817,6 +817,11 @@ public:
return true;
}
void createLongTailCall(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) override {
createShortJmp(Seq, Target, Ctx, /*IsTailCall*/ true);
}
bool convertJmpToTailCall(MCInst &Inst) override {
setTailCall(Inst);
return true;
@ -908,7 +913,7 @@ public:
}
void createLongJmp(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) const override {
MCContext *Ctx, bool IsTailCall) override {
// ip0 (r16) is reserved to the linker (refer to 5.3.1.1 of "Procedure Call
// Standard for the ARM 64-bit Architecture (AArch64)".
// The sequence of instructions we create here is the following:
@ -959,40 +964,29 @@ public:
Inst.clear();
Inst.setOpcode(AArch64::BR);
Inst.addOperand(MCOperand::createReg(AArch64::X16));
if (IsTailCall)
setTailCall(Inst);
Seq.emplace_back(Inst);
}
void createShortJmp(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) const override {
MCContext *Ctx, bool IsTailCall) override {
// ip0 (r16) is reserved to the linker (refer to 5.3.1.1 of "Procedure Call
// Standard for the ARM 64-bit Architecture (AArch64)".
// The sequence of instructions we create here is the following:
// movz ip0, #:abs_g1_nc:<addr>
// movk ip0, #:abs_g0_nc:<addr>
// adrp ip0, imm
// add ip0, ip0, imm
// br ip0
MCInst Inst;
Inst.setOpcode(AArch64::MOVZXi);
Inst.addOperand(MCOperand::createReg(AArch64::X16));
Inst.addOperand(MCOperand::createExpr(AArch64MCExpr::create(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx),
AArch64MCExpr::VK_ABS_G1_NC, *Ctx)));
Inst.addOperand(MCOperand::createImm(0x10));
Seq.emplace_back(Inst);
Inst.clear();
Inst.setOpcode(AArch64::MOVKXi);
Inst.addOperand(MCOperand::createReg(AArch64::X16));
Inst.addOperand(MCOperand::createReg(AArch64::X16));
Inst.addOperand(MCOperand::createExpr(AArch64MCExpr::create(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx),
AArch64MCExpr::VK_ABS_G0_NC, *Ctx)));
Inst.addOperand(MCOperand::createImm(0));
Seq.emplace_back(Inst);
MCPhysReg Reg = AArch64::X16;
std::vector<MCInst> Insts = materializeAddress(Target, Ctx, Reg);
Insts.emplace_back();
MCInst &Inst = Insts.back();
Inst.clear();
Inst.setOpcode(AArch64::BR);
Inst.addOperand(MCOperand::createReg(AArch64::X16));
Seq.emplace_back(Inst);
Inst.addOperand(MCOperand::createReg(Reg));
if (IsTailCall)
setTailCall(Inst);
Seq.swap(Insts);
}
/// Matching pattern here is
@ -1106,7 +1100,7 @@ public:
std::vector<MCInst> materializeAddress(const MCSymbol *Target, MCContext *Ctx,
MCPhysReg RegName,
int64_t Addend) const override {
int64_t Addend = 0) const override {
// Get page-aligned address and add page offset
std::vector<MCInst> Insts(2);
Insts[0].setOpcode(AArch64::ADRP);

View File

@ -3299,6 +3299,13 @@ public:
return createDirectCall(Inst, Target, Ctx, /*IsTailCall*/ true);
}
void createLongTailCall(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) override {
Seq.clear();
Seq.emplace_back();
createDirectCall(Seq.back(), Target, Ctx, /*IsTailCall*/ true);
}
bool createTrap(MCInst &Inst) const override {
Inst.clear();
Inst.setOpcode(X86::TRAP);
@ -3407,12 +3414,14 @@ public:
}
void createShortJmp(std::vector<MCInst> &Seq, const MCSymbol *Target,
MCContext *Ctx) const override {
MCContext *Ctx, bool IsTailCall) override {
Seq.clear();
MCInst Inst;
Inst.setOpcode(X86::JMP_1);
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx)));
if (IsTailCall)
setTailCall(Inst);
Seq.emplace_back(Inst);
}