From 4d4dfbc456e516e49f060f6a15affa46fdc6334a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 13 Sep 2021 23:13:30 +0100 Subject: [PATCH] Add RJUMP --- libevmasm/Assembly.cpp | 26 +++++++++++++++++++++++--- libevmasm/Assembly.h | 6 ++++++ libevmasm/AssemblyItem.cpp | 5 ++++- libevmasm/AssemblyItem.h | 8 +++++--- libevmasm/Instruction.cpp | 4 ++++ libevmasm/Instruction.h | 2 ++ 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 5b6e2a86f..d16f482c0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -533,11 +533,13 @@ LinkerObject const& Assembly::assemble() const unsigned bytesRequiredForCode = codeSize(static_cast(subTagSize)); m_tagPositionsInBytecode = vector(m_usedTags, numeric_limits::max()); map> tagRef; + set isStaticTagRef; multimap dataRef; multimap subRef; vector sizeRef; ///< Pointers to code locations where the size of the program is inserted unsigned bytesPerTag = numberEncodingSize(bytesRequiredForCode); uint8_t tagPush = static_cast(pushInstruction(bytesPerTag)); + constexpr unsigned bytesPerStaticTag = 2; unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + static_cast(m_auxiliaryData.size()); for (auto const& sub: m_subs) @@ -574,6 +576,16 @@ LinkerObject const& Assembly::assemble() const ret.bytecode.resize(ret.bytecode.size() + bytesPerTag); break; } + case StaticJump: + case StaticJumpI: + { + ret.bytecode.push_back(static_cast((i.type() == StaticJump) ? Instruction::RJUMP : Instruction::RJUMPI)); + ret.bytecode.push_back(tagPush); + tagRef[ret.bytecode.size()] = i.splitForeignPushTag(); + isStaticTagRef.insert(ret.bytecode.size()); + ret.bytecode.resize(ret.bytecode.size() + bytesPerStaticTag); + break; + } case PushData: ret.bytecode.push_back(dataRefPush); dataRef.insert(make_pair(h256(i.data()), ret.bytecode.size())); @@ -699,9 +711,17 @@ LinkerObject const& Assembly::assemble() const assertThrow(tagId < tagPositions.size(), AssemblyException, "Reference to non-existing tag."); size_t pos = tagPositions[tagId]; assertThrow(pos != numeric_limits::max(), AssemblyException, "Reference to tag without position."); - assertThrow(numberEncodingSize(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space."); - bytesRef r(ret.bytecode.data() + i.first, bytesPerTag); - toBigEndian(pos, r); + if (isStaticTagRef.count(i.first)) { + pos = pos - (i.first + bytesPerStaticTag); // TODO: do properly + assertThrow(numberEncodingSize(pos) <= bytesPerStaticTag, AssemblyException, "Tag too large for reserved space."); + //pos = static_cast(spos); + bytesRef r(ret.bytecode.data() + i.first, bytesPerStaticTag); + toBigEndian(pos, r); + } else { + assertThrow(numberEncodingSize(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space."); + bytesRef r(ret.bytecode.data() + i.first, bytesPerTag); + toBigEndian(pos, r); + } } for (auto const& [name, tagInfo]: m_namedTags) { diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 7b8b4509d..a4b31af8c 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -83,8 +83,14 @@ public: append(AssemblyItem(std::move(_data), _arguments, _returnVariables)); } + AssemblyItem appendStaticJump() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(StaticJump, m_usedTags++); } + AssemblyItem appendStaticJumpI() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(StaticJumpI, m_usedTags++); } +// AssemblyItem appendJump() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(StaticJump, m_usedTags++); } +// AssemblyItem appendJumpI() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(StaticJumpI, m_usedTags++); } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } + AssemblyItem appendStaticJump(AssemblyItem const& _tag) { /*force typecheck*/_tag.pushTag(); return AssemblyItem(StaticJump, _tag.data()); } + AssemblyItem appendStaticJumpI(AssemblyItem const& _tag) { /*force typecheck*/_tag.pushTag(); return AssemblyItem(StaticJumpI, _tag.data()); } AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; } diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index a36e0ddb1..b9951ac4f 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -62,7 +62,7 @@ AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const pair AssemblyItem::splitForeignPushTag() const { - assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); + assertThrow(m_type == StaticJump || m_type == StaticJumpI || m_type == PushTag || m_type == Tag, util::Exception, ""); u256 combined = u256(data()); size_t subId = static_cast((combined >> 64) - 1); size_t tag = static_cast(combined & 0xffffffffffffffffULL); @@ -159,6 +159,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) } case VerbatimBytecode: return std::get<2>(*m_verbatimBytecode).size(); + case StaticJump: + case StaticJumpI: + return 3; default: break; } diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 4aef82d8b..f764ee858 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -50,7 +50,9 @@ enum AssemblyItemType PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor. AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code. - VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification. + VerbatimBytecode, ///< Contains data that is inserted into the bytecode code section without modification. + StaticJump, + StaticJumpI }; enum class Precision { Precise , Approximate }; @@ -91,8 +93,8 @@ public: AssemblyItem& operator=(AssemblyItem const&) = default; AssemblyItem& operator=(AssemblyItem&&) = default; - AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); } - AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(PushTag, data()); } + AssemblyItem tag() const { assertThrow(m_type == StaticJump || m_type == StaticJumpI || m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); } + AssemblyItem pushTag() const { assertThrow(m_type == StaticJump || m_type == StaticJumpI || m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(PushTag, data()); } /// Converts the tag to a subassembly tag. This has to be called in order to move a tag across assemblies. /// @param _subId the identifier of the subassembly the tag is taken from. AssemblyItem toSubAssemblyTag(size_t _subId) const; diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index e2ce48b7f..9d6c28536 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -93,6 +93,8 @@ std::map const solidity::evmasm::c_instructions = { "MSIZE", Instruction::MSIZE }, { "GAS", Instruction::GAS }, { "JUMPDEST", Instruction::JUMPDEST }, + { "RJUMP", Instruction::RJUMP }, + { "RJUMPI", Instruction::RJUMPI }, { "PUSH1", Instruction::PUSH1 }, { "PUSH2", Instruction::PUSH2 }, { "PUSH3", Instruction::PUSH3 }, @@ -240,6 +242,8 @@ static std::map const c_instructionInfo = { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::GAS, { "GAS", 0, 0, 1, false, Tier::Base } }, { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, Tier::Special } }, + { Instruction::RJUMP, { "RJUMP", 0, 0, 0, true, Tier::Mid } }, + { Instruction::RJUMPI, { "RJUMPI", 0, 1, 0, true, Tier::High } }, { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLow } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 5d4432ca5..fe54d8703 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -101,6 +101,8 @@ enum class Instruction: uint8_t MSIZE, ///< get the size of active memory GAS, ///< get the amount of available gas JUMPDEST, ///< set a potential jump destination + RJUMP, + RJUMPI, PUSH1 = 0x60, ///< place 1 byte item on stack PUSH2, ///< place 2 byte item on stack