mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
Mandatory JUMPDEST for jumps and new static jumps recognition strategy
This commit is contained in:
parent
86553cda81
commit
ed614c5d2a
3 changed files with 33 additions and 75 deletions
|
|
@ -44,6 +44,8 @@ struct i256
|
||||||
};
|
};
|
||||||
static_assert(sizeof(i256) == 32, "Wrong i265 size");
|
static_assert(sizeof(i256) == 32, "Wrong i265 size");
|
||||||
|
|
||||||
|
#define UNTESTED assert(false)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#include <boost/dynamic_bitset.hpp>
|
|
||||||
|
|
||||||
#include <llvm/ADT/PostOrderIterator.h>
|
#include <llvm/ADT/PostOrderIterator.h>
|
||||||
#include <llvm/IR/CFG.h>
|
#include <llvm/IR/CFG.h>
|
||||||
#include <llvm/IR/Module.h>
|
#include <llvm/IR/Module.h>
|
||||||
|
|
@ -44,17 +42,13 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
|
||||||
{
|
{
|
||||||
std::set<ProgramCounter> splitPoints; // Sorted collections of instruction indices where basic blocks start/end
|
std::set<ProgramCounter> splitPoints; // Sorted collections of instruction indices where basic blocks start/end
|
||||||
|
|
||||||
std::map<ProgramCounter, ProgramCounter> directJumpTargets;
|
|
||||||
std::vector<ProgramCounter> indirectJumpTargets;
|
std::vector<ProgramCounter> indirectJumpTargets;
|
||||||
boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1)));
|
|
||||||
|
|
||||||
splitPoints.insert(0); // First basic block
|
splitPoints.insert(0); // First basic block
|
||||||
validJumpTargets[0] = true;
|
|
||||||
|
|
||||||
for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr)
|
for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr)
|
||||||
{
|
{
|
||||||
ProgramCounter currentPC = curr - _bytecode.begin();
|
ProgramCounter currentPC = curr - _bytecode.begin();
|
||||||
validJumpTargets[currentPC] = true;
|
|
||||||
|
|
||||||
auto inst = Instruction(*curr);
|
auto inst = Instruction(*curr);
|
||||||
switch (inst)
|
switch (inst)
|
||||||
|
|
@ -62,21 +56,7 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
|
||||||
|
|
||||||
case Instruction::ANY_PUSH:
|
case Instruction::ANY_PUSH:
|
||||||
{
|
{
|
||||||
auto val = readPushData(curr, _bytecode.end());
|
readPushData(curr, _bytecode.end());
|
||||||
auto next = curr + 1;
|
|
||||||
if (next == _bytecode.end())
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto nextInst = Instruction(*next);
|
|
||||||
if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI)
|
|
||||||
{
|
|
||||||
// Create a block for the JUMP target.
|
|
||||||
ProgramCounter targetPC = val.ult(_bytecode.size()) ? val.getZExtValue() : _bytecode.size();
|
|
||||||
splitPoints.insert(targetPC);
|
|
||||||
|
|
||||||
ProgramCounter jumpPC = (next - _bytecode.begin());
|
|
||||||
directJumpTargets[jumpPC] = targetPC;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,15 +85,6 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove split points generated from jumps out of code or into data.
|
|
||||||
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
|
|
||||||
{
|
|
||||||
if (*it > _bytecode.size() || !validJumpTargets[*it])
|
|
||||||
it = splitPoints.erase(it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
|
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
|
||||||
{
|
{
|
||||||
auto beginInstIdx = *it;
|
auto beginInstIdx = *it;
|
||||||
|
|
@ -126,30 +97,8 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
|
||||||
m_badJumpBlock = std::unique_ptr<BasicBlock>(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder));
|
m_badJumpBlock = std::unique_ptr<BasicBlock>(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder));
|
||||||
m_jumpTableBlock = std::unique_ptr<BasicBlock>(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder));
|
m_jumpTableBlock = std::unique_ptr<BasicBlock>(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder));
|
||||||
|
|
||||||
for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it)
|
|
||||||
{
|
|
||||||
if (it->second >= _bytecode.size())
|
|
||||||
{
|
|
||||||
// Jumping out of code means STOP
|
|
||||||
m_directJumpTargets[it->first] = m_stopBB;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto blockIter = basicBlocks.find(it->second);
|
|
||||||
if (blockIter != basicBlocks.end())
|
|
||||||
{
|
|
||||||
m_directJumpTargets[it->first] = blockIter->second.llvm();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
clog(JIT) << "Bad JUMP at PC " << it->first
|
|
||||||
<< ": " << it->second << " is not a valid PC";
|
|
||||||
m_directJumpTargets[it->first] = m_badJumpBlock->llvm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
|
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
|
||||||
m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second);
|
m_jumpDests[*it] = basicBlocks.find(*it)->second.llvm();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::string const& _id)
|
std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::string const& _id)
|
||||||
|
|
@ -196,16 +145,15 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
|
||||||
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
|
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
|
||||||
|
|
||||||
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
|
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
|
||||||
if (m_indirectJumpTargets.size() > 0)
|
if (m_jumpDests.size() > 0)
|
||||||
{
|
{
|
||||||
auto dest = m_jumpTableBlock->localStack().pop();
|
auto dest = m_jumpTableBlock->localStack().pop();
|
||||||
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(),
|
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(), m_jumpDests.size());
|
||||||
m_indirectJumpTargets.size());
|
for (auto it = m_jumpDests.cbegin(); it != m_jumpDests.cend(); ++it)
|
||||||
for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it)
|
|
||||||
{
|
{
|
||||||
auto& bb = *it;
|
auto& bb = *it;
|
||||||
auto dest = Constant::get(bb->begin());
|
auto dest = Constant::get(it->first);
|
||||||
switchInstr->addCase(dest, bb->llvm());
|
switchInstr->addCase(dest, it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -596,16 +544,23 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
case Instruction::JUMP:
|
case Instruction::JUMP:
|
||||||
case Instruction::JUMPI:
|
case Instruction::JUMPI:
|
||||||
{
|
{
|
||||||
// Generate direct jump iff:
|
|
||||||
// 1. this is not the first instruction in the block
|
|
||||||
// 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH)
|
|
||||||
// Otherwise generate a indirect jump (a switch).
|
|
||||||
llvm::BasicBlock* targetBlock = nullptr;
|
llvm::BasicBlock* targetBlock = nullptr;
|
||||||
if (currentPC != _basicBlock.begin())
|
auto target = stack.pop();
|
||||||
|
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(target))
|
||||||
{
|
{
|
||||||
auto pairIter = m_directJumpTargets.find(currentPC);
|
auto&& c = constant->getValue();
|
||||||
if (pairIter != m_directJumpTargets.end())
|
if (c.ult(_bytecode.size()))
|
||||||
targetBlock = pairIter->second;
|
{
|
||||||
|
auto v = c.getZExtValue();
|
||||||
|
auto it = m_jumpDests.find(v);
|
||||||
|
if (it != m_jumpDests.end())
|
||||||
|
targetBlock = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetBlock)
|
||||||
|
{
|
||||||
|
targetBlock = m_badJumpBlock->llvm();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst == Instruction::JUMP)
|
if (inst == Instruction::JUMP)
|
||||||
|
|
@ -614,26 +569,30 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
{
|
{
|
||||||
// The target address is computed at compile time,
|
// The target address is computed at compile time,
|
||||||
// just pop it without looking...
|
// just pop it without looking...
|
||||||
stack.pop();
|
|
||||||
m_builder.CreateBr(targetBlock);
|
m_builder.CreateBr(targetBlock);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
stack.push(target);
|
||||||
m_builder.CreateBr(m_jumpTableBlock->llvm());
|
m_builder.CreateBr(m_jumpTableBlock->llvm());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else // JUMPI
|
else // JUMPI
|
||||||
{
|
{
|
||||||
stack.swap(1);
|
|
||||||
auto val = stack.pop();
|
auto val = stack.pop();
|
||||||
auto zero = Constant::get(0);
|
auto zero = Constant::get(0);
|
||||||
auto cond = m_builder.CreateICmpNE(val, zero, "nonzero");
|
auto cond = m_builder.CreateICmpNE(val, zero, "nonzero");
|
||||||
|
|
||||||
if (targetBlock)
|
if (targetBlock)
|
||||||
{
|
{
|
||||||
stack.pop();
|
|
||||||
m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock);
|
m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
UNTESTED;
|
||||||
|
stack.push(target);
|
||||||
m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock);
|
m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,8 @@ private:
|
||||||
/// Maps a program counter pc to a basic block that starts at pc (if any).
|
/// Maps a program counter pc to a basic block that starts at pc (if any).
|
||||||
std::map<ProgramCounter, BasicBlock> basicBlocks = {};
|
std::map<ProgramCounter, BasicBlock> basicBlocks = {};
|
||||||
|
|
||||||
/// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump.
|
/// Map of jump destinations
|
||||||
std::map<ProgramCounter, llvm::BasicBlock*> m_directJumpTargets = {};
|
std::map<ProgramCounter, llvm::BasicBlock*> m_jumpDests = {};
|
||||||
|
|
||||||
/// A list of possible blocks to which there may be indirect jumps.
|
|
||||||
std::vector<BasicBlock*> m_indirectJumpTargets = {};
|
|
||||||
|
|
||||||
/// Stop basic block - terminates execution with STOP code (0)
|
/// Stop basic block - terminates execution with STOP code (0)
|
||||||
llvm::BasicBlock* m_stopBB = nullptr;
|
llvm::BasicBlock* m_stopBB = nullptr;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue