From b418086ca755e57ad69965c22420d3cf0402206a Mon Sep 17 00:00:00 2001
From: Jason Hiser <jdhiser@gmail.com>
Date: Thu, 27 Dec 2018 20:58:25 -0500
Subject: [PATCH] added support for tbz/tbnz tramplines to far-away places.

---
 include/sizer/sizer_arm64.hpp |  10 ++-
 include/sizer/sizer_base.hpp  |  18 ++---
 include/sizer/sizer_x86.hpp   |   2 +
 include/zipr_impl.h           |   2 +
 src/sizer_arm64.cpp           |  73 ++++++++++++++++++-
 src/sizer_base.cpp            |  18 +++++
 src/sizer_x86.cpp             | 128 ++++++++++++++++++++++++++++++++++
 src/zipr.cpp                  |   4 ++
 8 files changed, 240 insertions(+), 15 deletions(-)

diff --git a/include/sizer/sizer_arm64.hpp b/include/sizer/sizer_arm64.hpp
index 8d2f5d5..0fdc6dc 100644
--- a/include/sizer/sizer_arm64.hpp
+++ b/include/sizer/sizer_arm64.hpp
@@ -3,9 +3,15 @@
 
 class ZiprSizerARM64_t : public ZiprSizerBase_t
 {
+	private:
+	RangeAddress_t TBZPlopDollopEntryWithTarget    (DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const;
+	RangeAddress_t DefaultPlopDollopEntryWithTarget(DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const;
+
 	public:
-		ZiprSizerARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj);
-                size_t DetermineInsnSize(libIRDB::Instruction_t*, bool account_for_jump = true) const override;
+	ZiprSizerARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj);
+	size_t DetermineInsnSize(libIRDB::Instruction_t*, bool account_for_jump = true) const override;
+	virtual RangeAddress_t PlopDollopEntryWithTarget(DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const override;
+
 
 };
 #endif
diff --git a/include/sizer/sizer_base.hpp b/include/sizer/sizer_base.hpp
index cadfab3..02176b9 100644
--- a/include/sizer/sizer_base.hpp
+++ b/include/sizer/sizer_base.hpp
@@ -1,6 +1,7 @@
 #ifndef SIZERBASE_HPP
 #define SIZERBASE_HPP
 
+class ZiprImpl_t;
 
 class ZiprSizerBase_t
 {
@@ -8,19 +9,12 @@ class ZiprSizerBase_t
 		ZiprSizerBase_t(Zipr_SDK::Zipr_t* p_zipr_obj, 
 				const size_t p_CALLBACK_TRAMPOLINE_SIZE, 
 				const size_t p_TRAMPOLINE_SIZE, 
-				const size_t p_LONG_PIN_SIZE, 
+				const size_t p_LONG_PIN_SIZE,
 				const size_t p_SHORT_PIN_SIZE,
 				const size_t p_ALIGNMENT
-				) :
-			memory_space(*dynamic_cast<zipr::ZiprMemorySpace_t*>(p_zipr_obj->GetMemorySpace())),
-			CALLBACK_TRAMPOLINE_SIZE(p_CALLBACK_TRAMPOLINE_SIZE),
-			TRAMPOLINE_SIZE         (p_TRAMPOLINE_SIZE         ),
-			LONG_PIN_SIZE           (p_LONG_PIN_SIZE           ),
-			SHORT_PIN_SIZE          (p_SHORT_PIN_SIZE          ),
-			ALIGNMENT               (p_ALIGNMENT               )
-		{
-		}
-		zipr::ZiprMemorySpace_t &memory_space;
+				) ;
+		ZiprMemorySpace_t &memory_space;
+		ZiprImpl_t &m_zipr_obj;
 	public:
 		// methods
 		/** Calculate entire size of a dollop and it's fallthroughs.
@@ -33,7 +27,7 @@ class ZiprSizerBase_t
 		virtual size_t DetermineDollopSizeInclFallthrough(Dollop_t *dollop) const;
 		virtual size_t DetermineInsnSize(libIRDB::Instruction_t*, bool account_for_jump = true) const =0;
 		virtual Range_t DoPlacement(size_t pminimum_valid_req_size) const;
-
+		virtual RangeAddress_t PlopDollopEntryWithTarget( DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const =0;
 
 		// maybe try to make these private/protected and provide getters eventually.
 		const size_t CALLBACK_TRAMPOLINE_SIZE;
diff --git a/include/sizer/sizer_x86.hpp b/include/sizer/sizer_x86.hpp
index 418f5ae..260672a 100644
--- a/include/sizer/sizer_x86.hpp
+++ b/include/sizer/sizer_x86.hpp
@@ -7,6 +7,8 @@ class ZiprSizerX86_t  : public ZiprSizerBase_t
 	public:
 		ZiprSizerX86_t(Zipr_SDK::Zipr_t* p_zipr_obj);
                 size_t DetermineInsnSize(libIRDB::Instruction_t*, bool account_for_jump = true) const override;
+		virtual RangeAddress_t PlopDollopEntryWithTarget( DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const override;
+
 
 };
 #endif
diff --git a/include/zipr_impl.h b/include/zipr_impl.h
index 77c6707..b7b4981 100644
--- a/include/zipr_impl.h
+++ b/include/zipr_impl.h
@@ -158,6 +158,8 @@ class ZiprImpl_t : public Zipr_t
 		virtual Zipr_SDK::RangeAddress_t PlaceUnplacedScoops(Zipr_SDK::RangeAddress_t max);
 		Stats_t* GetStats() { return m_stats; }
 		ZiprSizerBase_t* GetSizer() { return sizer; }
+		ZiprPatcherBase_t* GetPatcher() { return patcher; }
+		ZiprPinnerBase_t* GetPinner() { return pinner; }
 
 
 	private:
diff --git a/src/sizer_arm64.cpp b/src/sizer_arm64.cpp
index 8753185..47e567e 100644
--- a/src/sizer_arm64.cpp
+++ b/src/sizer_arm64.cpp
@@ -8,6 +8,12 @@ namespace zipr
 
 using namespace zipr ;
 
+static bool is_tbz_type(Instruction_t* insn)
+{
+	const auto d=DecodedInstruction_t(insn);
+	return d.getMnemonic()=="tbz" || d.getMnemonic()=="tbnz";
+
+}
 
 ZiprSizerARM64_t::ZiprSizerARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprSizerBase_t(p_zipr_obj,4,4,4,4,4)
 {
@@ -15,5 +21,70 @@ ZiprSizerARM64_t::ZiprSizerARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprSizerBase
 
 size_t ZiprSizerARM64_t::DetermineInsnSize(Instruction_t* insn, bool account_for_jump) const
 {
-	return account_for_jump ? 8 : 4;
+	// tbz plan needs 12 bytes.  other plans need 4
+	const auto size = is_tbz_type(insn) ? 12 : 4;
+	const auto jmp_size= account_for_jump ? 4 : 0;
+	return size + jmp_size;
+}
+
+RangeAddress_t ZiprSizerARM64_t::PlopDollopEntryWithTarget(
+        DollopEntry_t *entry,
+        RangeAddress_t override_place,
+        RangeAddress_t override_target) const
+{
+        assert(entry->TargetDollop());
+	if(is_tbz_type(entry->Instruction()))
+		return  TBZPlopDollopEntryWithTarget(entry,override_place,override_target);
+	return  DefaultPlopDollopEntryWithTarget(entry,override_place,override_target);
 }
+
+RangeAddress_t ZiprSizerARM64_t::TBZPlopDollopEntryWithTarget(
+        DollopEntry_t *entry,
+        RangeAddress_t override_place,
+        RangeAddress_t override_target) const
+{
+/* tbz plan:
+ * L0  tbz <args>, L2
+ * L1  b L3
+ * L2: b <target>
+ * L3: # fallthrough
+ */ 
+        const auto addr        = (override_place  == 0) ? entry->Place()                 : override_place;
+        const auto target_addr = (override_target == 0) ? entry->TargetDollop()->Place() : override_target;
+	const auto insn        = entry->Instruction();
+	const auto branch_bytes=string("\x00\x00\x00\x014",4);
+
+	// put the tbz first.
+	memory_space.PlopBytes(addr, insn->GetDataBits().c_str(), 4);
+	m_zipr_obj.GetPatcher()->ApplyPatch(addr,addr+8);// make it jump to L2
+
+	// now drop down a uncond jump for L1, and make it go to L3
+	// (i.e., jump around the jump to the target) 
+	memory_space.PlopBytes(addr+4, branch_bytes.c_str(), 4);
+	m_zipr_obj.GetPatcher()->ApplyPatch(addr+4,addr+12);// make it jump to +8
+
+	// lastly, put down the uncond jump at L2, and make it go to the target 
+	memory_space.PlopBytes(addr+8, branch_bytes.c_str(), 4);
+	m_zipr_obj.GetPatcher()->ApplyPatch(addr+8,target_addr);// make it jump to +8
+
+	return addr+12; /* address of fallthrough, L3 */
+
+}
+
+
+RangeAddress_t ZiprSizerARM64_t::DefaultPlopDollopEntryWithTarget(
+        DollopEntry_t *entry,
+        RangeAddress_t override_place,
+        RangeAddress_t override_target) const
+{
+        const auto addr        = (override_place  == 0) ? entry->Place()                 : override_place;
+        const auto target_addr = (override_target == 0) ? entry->TargetDollop()->Place() : override_target;
+	const auto insn        = entry->Instruction();
+
+	// plop instruction an d make it target the right address.
+	memory_space.PlopBytes(addr, insn->GetDataBits().c_str(), 4);
+	m_zipr_obj.GetPatcher()->ApplyPatch(addr, target_addr);
+
+	return addr+4;
+}
+
diff --git a/src/sizer_base.cpp b/src/sizer_base.cpp
index 7e5b6fc..1b19d86 100644
--- a/src/sizer_base.cpp
+++ b/src/sizer_base.cpp
@@ -26,6 +26,24 @@ unique_ptr<ZiprSizerBase_t> ZiprSizerBase_t::factory(Zipr_SDK::Zipr_t* p_zipr_ob
 	return unique_ptr<ZiprSizerBase_t>(ret);
 }
 
+ZiprSizerBase_t::ZiprSizerBase_t(Zipr_SDK::Zipr_t* p_zipr_obj,
+		const size_t p_CALLBACK_TRAMPOLINE_SIZE,
+		const size_t p_TRAMPOLINE_SIZE,
+		const size_t p_LONG_PIN_SIZE,
+		const size_t p_SHORT_PIN_SIZE,
+		const size_t p_ALIGNMENT
+		) :
+	memory_space(*dynamic_cast<zipr::ZiprMemorySpace_t*>(p_zipr_obj->GetMemorySpace())),
+	m_zipr_obj(*dynamic_cast<zipr::ZiprImpl_t*>(p_zipr_obj)),
+	CALLBACK_TRAMPOLINE_SIZE(p_CALLBACK_TRAMPOLINE_SIZE),
+	TRAMPOLINE_SIZE         (p_TRAMPOLINE_SIZE         ),
+	LONG_PIN_SIZE           (p_LONG_PIN_SIZE           ),
+	SHORT_PIN_SIZE          (p_SHORT_PIN_SIZE          ),
+	ALIGNMENT               (p_ALIGNMENT               )
+{
+}
+
+
 Range_t ZiprSizerBase_t::DoPlacement(const size_t size) const
 {
 	auto new_place=memory_space.GetFreeRange(size+ALIGNMENT-1);	
diff --git a/src/sizer_x86.cpp b/src/sizer_x86.cpp
index 6867332..756505c 100644
--- a/src/sizer_x86.cpp
+++ b/src/sizer_x86.cpp
@@ -78,3 +78,131 @@ size_t ZiprSizerX86_t::DetermineInsnSize(Instruction_t* insn, bool account_for_j
 	else
 		return required_size;
 }
+
+RangeAddress_t ZiprSizerX86_t::PlopDollopEntryWithTarget(
+        DollopEntry_t *entry,
+        RangeAddress_t override_place,
+        RangeAddress_t override_target) const
+{
+        auto insn = entry->Instruction();
+
+        assert(entry->TargetDollop());
+
+        auto addr = entry->Place();
+        auto target_addr = entry->TargetDollop()->Place();
+        auto ret = addr;
+
+        if (override_place != 0)
+                addr = ret = override_place;
+
+        if (override_target != 0)
+                target_addr = override_target;
+
+        if(insn->GetDataBits().length() >2)
+        {
+                memory_space.PlopBytes(ret,
+                                       insn->GetDataBits().c_str(),
+                                       insn->GetDataBits().length()
+                                      );
+                m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr);
+                ret+=insn->GetDataBits().length();
+                return ret;
+        }
+
+        // call, jmp, jcc of length 2.
+        char b=insn->GetDataBits()[0];
+        switch(b)
+        {
+                case (char)0x70:
+                case (char)0x71:
+                case (char)0x72:
+                case (char)0x73:
+                case (char)0x74:
+                case (char)0x75:
+                case (char)0x76:
+                case (char)0x77:
+                case (char)0x78:
+                case (char)0x79:
+                case (char)0x7a:
+                case (char)0x7b:
+                case (char)0x7c:
+                case (char)0x7d:
+                case (char)0x7e:
+                case (char)0x7f:
+                {
+                // two byte JCC
+                        char bytes[]={(char)0x0f,(char)0xc0,(char)0x0,(char)0x0,(char)0x0,(char)0x0 };  // 0xc0 is a placeholder, overwritten next statement
+                        bytes[1]=insn->GetDataBits()[0]+0x10;           // convert to jcc with 4-byte offset.
+                        memory_space.PlopBytes(ret,bytes, sizeof(bytes));
+                        m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr);
+                        ret+=sizeof(bytes);
+                        return ret;
+                }
+                case (char)0xeb:
+                {
+                        // two byte JMP
+                        char bytes[]={(char)0xe9,(char)0x0,(char)0x0,(char)0x0,(char)0x0 };
+                        bytes[1]=insn->GetDataBits()[0]+0x10;           // convert to jcc with 4-byte offset.
+                        memory_space.PlopBytes(ret,bytes, sizeof(bytes));
+                        m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr);
+                        ret+=sizeof(bytes);
+                        return ret;
+                }
+		                case (char)0xe0:
+                case (char)0xe1:
+                case (char)0xe2:
+                case (char)0xe3:
+                {
+                        // loop, loopne, loopeq, jecxz
+                        // convert to:
+                        // <op> +5:
+                        // jmp fallthrough
+                        // +5: jmp target
+                        char bytes[]={0,0x5};
+                        DollopEntry_t *fallthrough_de = NULL;
+
+                        fallthrough_de = entry->MemberOfDollop()->FallthroughDollopEntry(entry);
+
+                        /*
+                         * This means that we have a fallthrough for this dollop entry
+                         * that is actually the fallthrough of the dollop! Wowser.
+                         * TODO: Before doing this, check to make sure that _entry_
+                         * is the last of the dollop.
+                         */
+                        if (!fallthrough_de)
+                        {
+                                if(entry->MemberOfDollop()->FallthroughDollop())
+                                        fallthrough_de = entry->MemberOfDollop()->FallthroughDollop()->front();
+                                else
+                                        // even a cond branch may have a null fallthrough, account for that here
+                                        // by plopping nothing
+                                        return ret;
+                        }
+
+                        assert(fallthrough_de && fallthrough_de->IsPlaced());
+
+                        bytes[0]=insn->GetDataBits()[0];
+                        memory_space.PlopBytes(ret,bytes, sizeof(bytes));
+			                        memory_space.PlopBytes(ret,bytes, sizeof(bytes));
+                        ret+=sizeof(bytes);
+
+                        memory_space.PlopJump(ret);
+                        m_zipr_obj.GetPatcher()->ApplyPatch(ret, fallthrough_de->Place());
+                        ret+=5;
+
+                        memory_space.PlopJump(ret);
+                        m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr);
+                        ret+=5;
+
+                        return ret;
+
+                }
+
+                default:
+                        assert(0);
+        }
+}
+
+
+
+
diff --git a/src/zipr.cpp b/src/zipr.cpp
index 9dd854e..8b9bddc 100644
--- a/src/zipr.cpp
+++ b/src/zipr.cpp
@@ -1949,6 +1949,9 @@ RangeAddress_t ZiprImpl_t::PlopDollopEntryWithTarget(
 	RangeAddress_t override_place,
 	RangeAddress_t override_target)
 {
+	return sizer->PlopDollopEntryWithTarget(entry,override_place,override_target);
+#if 0
+
 	auto insn = entry->Instruction();
 
 	assert(entry->TargetDollop());
@@ -2067,6 +2070,7 @@ RangeAddress_t ZiprImpl_t::PlopDollopEntryWithTarget(
 		default:
 			assert(0);
 	}
+#endif
 }
 
 RangeAddress_t ZiprImpl_t::PlopDollopEntryWithCallback(
-- 
GitLab