From 271ae947c0df9fa94a2ee4d2648aeed5fcbfbd05 Mon Sep 17 00:00:00 2001
From: Jason Hiser <jdhiser@gmail.com>
Date: Thu, 9 May 2019 14:16:17 -0400
Subject: [PATCH] added vldr patching support

---
 unpin_arm32.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 89 insertions(+), 2 deletions(-)

diff --git a/unpin_arm32.cpp b/unpin_arm32.cpp
index 03c7980..2fe029a 100644
--- a/unpin_arm32.cpp
+++ b/unpin_arm32.cpp
@@ -119,6 +119,7 @@ void UnpinArm32_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* relo
 
 	// so far, only handling ldr and ldrb
 	const auto is_ldr_type   = mnemonic.substr(0,3)=="ldr";         // ldr, ldrb, ldreq, ldrbeq, etc.
+	const auto is_vldr_type  = mnemonic.substr(0,4)=="vldr";        // vldr <double>, vldr <single> 
 	const auto is_add_type   = mnemonic.substr(0,3)=="add";         // add, addne, etc.
 	const auto is_addne_type = mnemonic == "addne";                 // exactly addne
 	const auto is_addls_type = mnemonic == "addls";                	// exactly addls
@@ -137,7 +138,93 @@ void UnpinArm32_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* relo
 	assert(tmp_reg < 15); // sanity check 4 bits
 
 
-	if(is_ldr_type && !is_rd_pc)
+	if(is_vldr_type)
+	{
+		/* 
+		 * We need to patch an vldr[b][cond] fp_reg, [pc + constant]
+		 * to be at a new location.
+		 *
+		 * The plan :
+		 * FA: b<cond> L0
+		 * FT:
+		 * ..
+		 * L0  str     rd, [sp,#-fc]
+		 * L1  ldr     rd, [L6]
+		 * L2  add     rd, pc, rd 
+		 * L3  vldr    rd, [rd, <imm>] # orig insn
+		 * L4  ldr     rd, [sp,#-fc]
+		 * L5: b       FT
+		 * L6: .word <constant>
+		 */
+		const auto tramp_size  = 6*4 + 4 ; // 6 insns, 4 bytes each, plus one word of read-only data
+		const auto tramp_range = ms.getFreeRange(tramp_size);
+		const auto tramp_start = tramp_range.getStart();
+		// don't be too fancy, just reserve 16 bytes.
+		ms.splitFreeRange({tramp_start,tramp_start+tramp_size});
+
+		// and give the bytes some names
+		const auto FA=from_insn_location;
+		const auto FT=from_insn_location+4;
+		const auto L0 = tramp_start;
+		const auto L1 = L0 + 4;
+		const auto L2 = L1 + 4;
+		const auto L3 = L2 + 4;
+		const auto L4 = L3 + 4;
+		const auto L5 = L4 + 4;
+		const auto L6 = L5 + 4;
+
+		// Create a branch to put over the original ldr 
+		// and set the conditional bits equal to 
+		// the original instruction conditional bits
+		auto my_branch_bytes = branch_bytes;
+		my_branch_bytes[3]  &= 0x0f; // clear always condition bits
+		my_branch_bytes[3]  |= (insn_bytes[3] & 0xf0); // add the vldr cond bits.
+		ms.plopBytes(FA,my_branch_bytes.c_str(),4);
+		// and make it point at L0
+		zo->applyPatch(FA,L0);
+
+		// spill tmp_reg at L0, e50d00fc
+		auto  spill_insn = string("\xfc\x00\x0d\xe5",4);
+		spill_insn[1]   |= (tmp_reg<<4);
+		ms.plopBytes(L0,spill_insn.c_str(),4);
+
+		// ldr dest_reg, [pc+k] (where pc+8+k == L6)
+		auto ldr_imm_insn = string("\x0c\x00\x9f\xe5",4);
+		ldr_imm_insn[1]  |= (tmp_reg << 4); // set this instruction's dest reg to the ldr's dest reg.
+		ms.plopBytes(L1,ldr_imm_insn.c_str(),4);
+
+		// put down add tmp_reg,pc, temp_reg
+		auto new_add_word = string("\x00\x00\x8f\xe0",4);   // e08f0000	 add r0, pc, r0
+		new_add_word[1]  |= (tmp_reg<<4);
+		new_add_word[0]  |= (tmp_reg<<0);
+		ms.plopBytes(L2,new_add_word.c_str(),4);
+
+		// put down orig vldr insn, with 1) cond field removed, and 2) Rn set to -> tmp_reg
+		auto vldr_insn_bytes = from_insn->getDataBits();
+		vldr_insn_bytes[2] &= 0xf0;           // remove Rn bits.
+		vldr_insn_bytes[2] |= (tmp_reg << 0); // set Rn to tmp-reg
+		ms.plopBytes(L3,vldr_insn_bytes.c_str(),4);
+
+		// put down L5, restore of scratch reg r0
+		auto  restore_insn = string("\xfc\x00\x1d\xe5",4);
+		restore_insn[1]   |= (tmp_reg<<4); // set Rd field
+		ms.plopBytes(L4,restore_insn.c_str(),4);
+
+		// put an uncond branch the end of the trampoline
+		// and make it jump at FT
+		ms.plopBytes(L5,branch_bytes.c_str(),4);
+		zo->applyPatch(L5,FT);
+
+		// put the calculated pc-rel offset at L3
+		const auto new_offset    = int32_t(orig_insn_addr - L2);
+		ms.plopBytes(L6,reinterpret_cast<const char*>(&new_offset),4);	// endianness of host must match target
+
+		// should be few enough of these to always print
+		cout<< "Had to trampoline " << disasm->getDisassembly() << " @"<<FA<<" to "
+		    << hex << L0 << "-" << L0+tramp_size-1 << endl;
+
+	}
+	else if( is_ldr_type && !is_rd_pc)
 	{
 		/* 
 		 * We need to patch an ldr[b][cond] reg, [pc + constant]
@@ -257,7 +344,7 @@ void UnpinArm32_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* relo
 		// and make it point at L0
 		zo->applyPatch(FA,L0);
 
-		// spill reg at L0, e50d00fc
+		// spill tmp_reg at L0, e50d00fc
 		auto  spill_insn = string("\xfc\x00\x0d\xe5",4);
 		spill_insn[1]   |= (tmp_reg<<4);
 		ms.plopBytes(L0,spill_insn.c_str(),4);
-- 
GitLab