diff --git a/zipr_unpin_plugin/.gitattributes b/zipr_unpin_plugin/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..3543f6aefae387a8eb6cee4237709f702e77d3b3
--- /dev/null
+++ b/zipr_unpin_plugin/.gitattributes
@@ -0,0 +1,5 @@
+* text=auto !eol
+/SConscript -text
+/SConstruct -text
+/unpin.cpp -text
+/unpin.h -text
diff --git a/zipr_unpin_plugin/.gitignore b/zipr_unpin_plugin/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ebc2f29f0152cb43dfc3bdf5286fe818bf10c6c9
--- /dev/null
+++ b/zipr_unpin_plugin/.gitignore
@@ -0,0 +1,7 @@
+.sconsign.dblite
+build
+*.swp
+
+*.os
+*.o
+*.zpi
diff --git a/zipr_unpin_plugin/LICENSE b/zipr_unpin_plugin/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..7ab9c0ee27c2b4f09b23feafcf3ce802bb59665f
--- /dev/null
+++ b/zipr_unpin_plugin/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2014-2021, Zephyr Software LLC
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/zipr_unpin_plugin/SConscript b/zipr_unpin_plugin/SConscript
new file mode 100644
index 0000000000000000000000000000000000000000..dddb4cad6fc8fd06b57dde01f58f4f0a971b81a2
--- /dev/null
+++ b/zipr_unpin_plugin/SConscript
@@ -0,0 +1,69 @@
+import shutil
+import os
+import tarfile
+
+Import('env')
+
+(sysname, nodename, release, version, machine)=os.uname()
+
+
+
+#print 'env='
+#print env.Dump()
+
+
+
+myenv=env
+myenv.Replace(SECURITY_TRANSFORMS_HOME=os.environ['SECURITY_TRANSFORMS_HOME'])
+myenv.Replace(ZIPR_HOME=os.environ['ZIPR_HOME'])
+myenv.Replace(IRDB_SDK=os.environ['IRDB_SDK'])
+myenv.Replace(ZIPR_SDK=os.environ['ZIPR_SDK'])
+myenv.Replace(do_cgc=ARGUMENTS.get("do_cgc",0))
+
+files=  '''
+	unpin.cpp
+	unpin_aarch64.cpp
+	unpin_arm32.cpp
+	unpin_mips32.cpp
+	unpin_x86.cpp
+	'''
+
+# ELFIO needs to be first so we get the zipr version instead of the sectrans version.  the zipr version is modified to include get_offset.
+cpppath=''' 
+	.
+	$ZIPR_HOME/third_party/ELFIO/elfio-2.2	
+	$IRDB_SDK/include/
+	$SECURITY_TRANSFORMS_HOME/include
+	$SECURITY_TRANSFORMS_HOME/libIRDB/include
+	$SECURITY_TRANSFORMS_HOME/libtransform/include
+	$ZIPR_HOME/include/
+	$ZIPR_SDK/include/
+	'''
+
+libs='''
+	'''
+
+libpath='''
+	$SECURITY_TRANSFORMS_HOME/lib
+	'''
+
+if sysname != "SunOS":
+	myenv.Append(CCFLAGS=" -Wall -Werror -fmax-errors=2")
+
+myenv.Append(CXXFLAGS=" -std=c++11 ")
+myenv=myenv.Clone(CPPPATH=Split(cpppath), LIBS=Split(libs), LIBPATH=Split(libpath), SHLIBSUFFIX=".zpi", SHLIBPREFIX="")
+lib=myenv.SharedLibrary("unpin", Split(files))
+
+install=myenv.Install("$PEASOUP_HOME/zipr_install/plugins/", lib)
+Default(install)
+
+ret=[install,lib]
+
+pedi = Command( target = "./unpin-testoutput",
+                source = install,
+                action = "cd "+os.environ['PEASOUP_HOME']+"/zipr_install ; " +os.environ['PEDI_HOME']+"/pedi -m manifest.txt ; cd -" )
+
+if Dir('.').abspath == Dir('#.').abspath:
+	ret=ret+pedi
+Default( ret )
+Return('ret')
diff --git a/zipr_unpin_plugin/SConstruct b/zipr_unpin_plugin/SConstruct
new file mode 100644
index 0000000000000000000000000000000000000000..6b5cd6b97e922cc5aa24482950db5749869a4818
--- /dev/null
+++ b/zipr_unpin_plugin/SConstruct
@@ -0,0 +1,47 @@
+import os
+import sys
+
+(sysname, nodename, release, version, machine)=os.uname()
+
+
+
+
+env=Environment()
+
+# default build options
+env.Replace(CFLAGS="-fPIC  -w ")
+env.Replace(CXXFLAGS="-fPIC  -w ")
+env.Replace(LINKFLAGS="-fPIC  ")
+
+# parse arguments
+env.Replace(SECURITY_TRANSFORMS_HOME=os.environ['SECURITY_TRANSFORMS_HOME'])
+env.Replace(ZIPR_HOME=os.environ['ZIPR_HOME'])
+env.Replace(ZIPR_INSTALL=os.environ['ZIPR_INSTALL'])
+env.Replace(ZIPR_SDK=os.environ['ZIPR_SDK'])
+env.Replace(debug=ARGUMENTS.get("debug",0))
+env.Replace(do_64bit_build=ARGUMENTS.get("do_64bit_build",0))
+
+
+if int(env['debug']) == 1:
+        print "Setting debug mode"
+        env.Append(CFLAGS=" -g")
+        env.Append(CXXFLAGS=" -g")
+        env.Append(LINKFLAGS=" -g")
+else:
+        print "Setting release mode"
+        env.Append(CFLAGS=" -O3")
+        env.Append(CXXFLAGS=" -O3")
+        env.Append(LINKFLAGS=" -O3")
+
+if sysname == "SunOS":
+        env.Append(LINKFLAGS=" -L/opt/csw/lib -DSOLARIS  ")
+        env.Append(CFLAGS=" -I/opt/csw/include -DSOLARIS ")
+        env.Append(CXXFLAGS=" -I/opt/csw/include -DSOLARIS  ")
+
+
+env['build_appfw']=0
+env['build_tools']=0
+
+Export('env')
+SConscript("SConscript", variant_dir='build')
+
diff --git a/zipr_unpin_plugin/unpin.cpp b/zipr_unpin_plugin/unpin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3f30cb508e1324df6a32ad75ff7cc4b73fb1dcac
--- /dev/null
+++ b/zipr_unpin_plugin/unpin.cpp
@@ -0,0 +1,325 @@
+/***************************************************************************
+ * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
+ *
+ * This software is furnished under a license and/or other restrictive
+ * terms and may be used and copied only in accordance with such terms
+ * and the inclusion of the above copyright notice. This software or
+ * any other copies thereof may not be provided or otherwise made
+ * available to any other person without the express written consent
+ * of an authorized representative of Zephyr Software LCC. Title to,
+ * ownership of, and all rights in the software is retained by
+ * Zephyr Software LCC.
+ *
+ * Zephyr Software LLC. Proprietary Information
+ *
+ * Unless otherwise specified, the information contained in this
+ * directory, following this legend, and/or referenced herein is
+ * Zephyr Software LLC. (Zephyr) Proprietary Information.
+ *
+ * CONTACT
+ *
+ * For technical assistance, contact Zephyr Software LCC. at:
+ *
+ *
+ * Zephyr Software, LLC
+ * 2040 Tremont Rd
+ * Charlottesville, VA 22911
+ *
+ * E-mail: jwd@zephyr-software.com
+ **************************************************************************/
+
+
+#include <string>
+#include <cstring>
+#include <algorithm>
+#include "unpin.h"
+#include <memory>
+#include <inttypes.h>
+
+
+using namespace IRDB_SDK;
+using namespace std;
+using namespace Zipr_SDK;
+
+
+#define ALLOF(a) begin(a),end(a)
+
+bool Unpin_t::should_cfi_pin(Instruction_t* insn)
+{
+	// add command line option that:
+	// 	1) return false if !has_cfi_reloc(insn)
+	// 	2) return true if option is on.
+	return *m_should_cfi_pin;
+}
+
+// CAN BE DELETED, left in just for stats? (Would speed up zipr step to delete)
+void Unpin_t::DoUnpin()
+{
+	DoUnpinForScoops();
+	DoUnpinForFixedCalls();
+}
+
+// CAN BE DELETED, left in just for stats?
+// scan instructions and process instruction relocs that can be unpinned.
+void Unpin_t::DoUnpinForFixedCalls()
+{
+	if((int64_t)*m_max_unpins != (int64_t)-1 && (int64_t)unpins>=(int64_t)*m_max_unpins)
+		return;
+	auto insn_unpins=0;
+	auto missed_unpins=0;
+
+	for(auto from_insn : zo->getFileIR()->getInstructions())
+	{
+		for(auto reloc : from_insn->getRelocations())
+		{
+			// this probably won't work on shared objects.
+			// complicated with the push64-reloc plugin also rewriting these things?
+			if(reloc->getType()==string("32-bit") || reloc->getType()==string("push64"))
+			{
+				// skip if there's no WRT, that means it's unpinned for something besides a fixed call.
+				if(reloc->getWRT()==NULL)
+					continue;
+
+				// getWRT returns an BaseObj, but this reloc type expects an instruction
+				// safe cast and check.
+				auto wrt_insn=dynamic_cast<Instruction_t*>(reloc->getWRT());
+				assert(wrt_insn);
+		
+				unpins++;
+				insn_unpins++;
+				if((int64_t)*m_max_unpins != (int64_t)-1 && (int64_t)unpins>=(int64_t)*m_max_unpins)
+					return;
+			}
+		}
+	}
+
+	cout<<"# ATTRIBUTE Zipr_Unpinning::insn_unpin_total_unpins="<<dec<<insn_unpins<<endl;
+	cout<<"# ATTRIBUTE Zipr_Unpinning::insn_unpin_missed_unpins="<<dec<<missed_unpins<<endl;
+}
+
+// CAN BE DELETED, left in just for stats?
+void Unpin_t::DoUnpinForScoops()
+{
+	if((int64_t)*m_max_unpins != (int64_t)-1 && (int64_t)unpins>=(int64_t)*m_max_unpins)
+		return;
+
+	auto missed_unpins=0;
+	auto scoop_unpins=0;
+
+	for(auto scoop : zo->getFileIR()->getDataScoops())
+	{
+		for(auto reloc : scoop->getRelocations())
+		{
+			if(reloc->getType()==string("data_to_insn_ptr"))
+			{
+				auto insn=dynamic_cast<Instruction_t*>(reloc->getWRT());
+				// getWRT returns an BaseObj, but this reloc type expects an instruction
+				// safe cast and check.
+				assert(insn);
+
+				unpins++;
+				scoop_unpins++;
+				if((int64_t)*m_max_unpins != (int64_t)-1 && (int64_t)unpins>=(int64_t)*m_max_unpins)
+					return;
+			}
+		}
+	}
+
+	cout<<"# ATTRIBUTE Zipr_Unpinning::scoop_unpin_total_unpins="<<dec<<scoop_unpins<<endl;
+	cout<<"# ATTRIBUTE Zipr_Unpinning::scoop_unpin_missed_unpins="<<dec<<missed_unpins<<endl;
+}
+
+Zipr_SDK::ZiprPreference Unpin_t::retargetCallback(
+	const RangeAddress_t &callback_address,
+	const DollopEntry_t *callback_entry,
+	RangeAddress_t &target_address)
+{
+	if(!*m_on) return Zipr_SDK::ZiprPluginInterface_t::retargetCallback(callback_address, callback_entry, target_address);
+
+	unpins++;// unpinning a call to a scoop.
+	if((int64_t)*m_max_unpins != (int64_t)-1 && (int64_t)unpins>=(int64_t)*m_max_unpins)
+		return Zipr_SDK::ZiprPluginInterface_t::retargetCallback(callback_address, callback_entry, target_address);
+
+
+	auto  insn = callback_entry->getInstruction();
+	for(auto reloc : insn->getRelocations())
+	{
+		if (reloc->getType()==string("callback_to_scoop"))
+		{
+			auto wrt = dynamic_cast<DataScoop_t*>(reloc->getWRT());
+			auto addend = reloc->getAddend();
+
+			target_address = wrt->getStart()->getVirtualOffset() + addend;
+		
+			if (*m_verbose) {
+				cout << "Unpin::callback_to_scoop: target_addr "
+				     << std::hex << target_address << endl;
+			}
+		}
+	}
+	return Must;
+}
+
+void Unpin_t::DoUpdate()
+{
+	DoUpdateForScoops();
+	DoUpdateForInstructions();
+}
+
+
+// scan for instructions that were placed, and now need an update.
+void Unpin_t::DoUpdateForInstructions()
+{
+	for(auto from_insn : zo->getFileIR()->getInstructions())
+	{
+		for(auto reloc : from_insn->getRelocations())
+		{
+			// this probably won't work on shared objects.
+			// complicated with the push64-reloc plugin also rewriting these things?
+			if(reloc->getType()==string("32-bit") || reloc->getType()==string("push64"))
+				HandleRetAddrReloc(from_insn,reloc);
+
+			// instruction has a pcrel memory operand.
+			else if(reloc->getType()==string("pcrel")) //  && reloc->getWRT()!=NULL)
+				HandlePcrelReloc(from_insn,reloc);
+
+			// instruction has a absolute  memory operand that needs it's displacement updated.
+			else if(reloc->getType()==string("absoluteptr_to_scoop"))
+				HandleAbsptrReloc(from_insn,reloc);
+
+			// instruction has an immediate that needs an update.
+			else if(reloc->getType()==string("immedptr_to_scoop"))
+				HandleImmedptrReloc(from_insn,reloc);
+
+			// deal with a callback, think this isn't used anymore
+			else if(reloc->getType()==string("callback_to_scoop"))
+				HandleCallbackReloc(from_insn,reloc);
+		}
+	}
+}
+
+void Unpin_t::DoUpdateForScoops()
+{
+	auto byte_width=zo->getFileIR()->getArchitectureBitWidth()/8;
+	for(auto scoop : zo->getFileIR()->getDataScoops())
+	{
+		if(scoop->isExecuteable()) continue;
+		assert(scoop->getEnd()->getVirtualOffset() - scoop->getStart()->getVirtualOffset()+1 == scoop->getSize());
+		assert(scoop->getContents().size() == scoop->getSize());
+		auto scoop_contents=scoop->getContents();
+
+		for(auto reloc : scoop->getRelocations())
+		{
+			if(reloc->getType()==string("data_to_insn_ptr"))
+			{
+				VirtualOffset_t reloff=reloc->getOffset();
+				Instruction_t* insn=dynamic_cast<Instruction_t*>(reloc->getWRT());
+				// getWRT returns an BaseObj, but this reloc type expects an instruction
+				// safe cast and check.
+				assert(insn);
+				Zipr_SDK::InstructionLocationMap_t &locMap=*(zo->getLocationMap());
+				IRDB_SDK::VirtualOffset_t newLoc=locMap[insn];
+
+				cout<<"Unpin::Unpinned data_to_insn_ptr insn ("<<hex<<insn->getBaseID()<<":"
+				    <<insn->getDisassembly()<<") with offset="<<hex<<reloc->getOffset()
+				    <<".  Insn moved to "<<hex<<newLoc<<endl;
+
+				bool found=should_cfi_pin(insn);
+
+				/* don't unpin if we found one */
+				if(found)
+				{
+					cout<<"Unpin::Skipping update because CFI is requesting a nonce."<<endl;
+				}
+				else
+				{
+					// determine how big the ptr is.
+					const int ptrsize=zo->getFileIR()->getArchitectureBitWidth()/8;
+					char addr[ptrsize];
+					memset(addr,0,ptrsize);
+		
+					// convert it to bytes.
+					switch(ptrsize)
+					{
+						case 4:
+						{
+							const auto newVal=(int)newLoc;
+							memcpy(addr,&newVal,ptrsize);
+							break;
+						}
+						case 8:
+						{
+							const auto newVal=(long long)newLoc;
+							memcpy(addr,&newVal,ptrsize);
+							break;
+						}
+						default:
+							assert(0);
+					}
+					// copy in new ptr.
+					for(int i=0;i<ptrsize;i++)
+						scoop_contents[reloff+i]=addr[i];
+				}
+				
+			}
+			else if(reloc->getType()==string("dataptr_to_scoop"))
+			{
+				DataScoop_t *wrt=dynamic_cast<DataScoop_t*>(reloc->getWRT());
+				assert(wrt);
+
+				VirtualOffset_t val_to_patch=0;
+                		const char* data=scoop_contents.c_str();
+
+				if(byte_width==4)
+				{
+					auto newVal=(int)0;
+					memcpy(&newVal, &data[reloc->getOffset()], byte_width);
+					val_to_patch=newVal;
+				}
+				else if(byte_width==8)
+				{
+					auto newVal=(long long)0;
+					memcpy(&newVal, &data[reloc->getOffset()], byte_width);
+					val_to_patch=newVal;
+				}
+				else
+					assert(0);
+
+				// wrt scoop should be placed.
+				assert(wrt->getStart()->getVirtualOffset() !=0 );
+				VirtualOffset_t new_val_to_patch=val_to_patch + wrt->getStart()->getVirtualOffset();
+
+                                if(byte_width==4)
+                                {
+                                        unsigned int intnewval=(unsigned int)new_val_to_patch;     // 64->32 narrowing OK. 
+                                        scoop_contents.replace(reloc->getOffset(), byte_width, (char*)&intnewval, byte_width);
+                                }
+                                else if(byte_width==8)
+                                {
+                                        scoop_contents.replace(reloc->getOffset(), byte_width, (char*)&new_val_to_patch, byte_width);
+                                }
+                                else
+                                        assert(0);
+
+				cout<<"Patched "<<scoop->getName()<<"+"<<hex<<reloc->getOffset()<<" to value "<<hex<<new_val_to_patch<<endl;
+			}
+		}
+		scoop->setContents(scoop_contents);
+	}
+}
+
+
+extern "C" 
+Zipr_SDK::ZiprPluginInterface_t* GetPluginInterface(
+	Zipr_SDK::Zipr_t* zipr_object)
+{
+	const auto mt=zipr_object->getFileIR()->getArchitecture()->getMachineType();
+
+	return 
+		mt==admtX86_64  ? (Unpin_t*)new UnpinX86_t    (zipr_object) :
+		mt==admtI386    ? (Unpin_t*)new UnpinX86_t    (zipr_object) :
+		mt==admtAarch64 ? (Unpin_t*)new UnpinAarch64_t(zipr_object) :
+		mt==admtArm32   ? (Unpin_t*)new UnpinArm32_t  (zipr_object) :
+		mt==admtMips32  ? (Unpin_t*)new UnpinMips32_t (zipr_object) :
+		throw invalid_argument("Cannot determine machine type");
+}
diff --git a/zipr_unpin_plugin/unpin.h b/zipr_unpin_plugin/unpin.h
new file mode 100644
index 0000000000000000000000000000000000000000..9659800dfaf3ef214ff8a34e641f6c4b5e6edd0f
--- /dev/null
+++ b/zipr_unpin_plugin/unpin.h
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
+ *
+ * This software is furnished under a license and/or other restrictive
+ * terms and may be used and copied only in accordance with such terms
+ * and the inclusion of the above copyright notice. This software or
+ * any other copies thereof may not be provided or otherwise made
+ * available to any other person without the express written consent
+ * of an authorized representative of Zephyr Software LCC. Title to,
+ * ownership of, and all rights in the software is retained by
+ * Zephyr Software LCC.
+ *
+ * Zephyr Software LLC. Proprietary Information
+ *
+ * Unless otherwise specified, the information contained in this
+ * directory, following this legend, and/or referenced herein is
+ * Zephyr Software LLC. (Zephyr) Proprietary Information.
+ *
+ * CONTACT
+ *
+ * For technical assistance, contact Zephyr Software LCC. at:
+ *
+ *
+ * Zephyr Software, LLC
+ * 2040 Tremont Rd
+ * Charlottesville, VA 22911
+ *
+ * E-mail: jwd@zephyr-software.com
+ **************************************************************************/
+
+#ifndef unpin_h
+#define unpin_h
+
+#include <irdb-core>
+#include <zipr-sdk>
+
+class Unpin_t : public Zipr_SDK::ZiprPluginInterface_t
+{
+	public:
+		Unpin_t( Zipr_SDK::Zipr_t* zipr_object) 
+			: 
+				zo(zipr_object), 
+				unpins(0),
+		                missed_unpins(0),
+                		ms(*zo->getMemorySpace()),
+                		locMap(*(zo->getLocationMap())),
+                		firp(*(zo->getFileIR()))
+
+		{ 
+
+			auto global=zo->getOptionsManager()->getNamespace("global");
+			auto unpin =zo->getOptionsManager()->getNamespace("unpin" );
+
+			m_verbose =        global->getBooleanOption("verbose");
+			m_should_cfi_pin = unpin ->getBooleanOption("should_cfi_pin", "Pin CFI instructions.", false);
+			m_on =             unpin ->getBooleanOption("on","Turn unpin plugin on/off.", true);
+			m_max_unpins =     unpin ->getIntegerOption("max-unpins","Set how many unpins are allowed, useful for debugging.",-1);
+
+		}
+
+		virtual ~Unpin_t() 
+		{ } 
+
+		virtual void doPinningBegin() override
+		{
+			if(!m_on) return;
+			DoUnpin();
+		}
+		virtual void doCallbackLinkingEnd() override
+		{
+			if(!m_on) return;
+			DoUpdate();
+		}
+
+		// virtual Zipr_SDK::ZiprOptionsNamespace_t *registerOptions(Zipr_SDK::ZiprOptionsNamespace_t *) override;
+
+		Zipr_SDK::ZiprPreference retargetCallback(
+			const Zipr_SDK::RangeAddress_t &callback_address,
+			const Zipr_SDK::DollopEntry_t *callback_entry,
+			Zipr_SDK::RangeAddress_t &target_address) override;
+	protected:
+		// designed for arch-specific override.
+		virtual void HandleRetAddrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc)=0;
+		virtual void HandlePcrelReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc)=0;
+		virtual void HandleAbsptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc)=0;
+		virtual void HandleImmedptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc)=0;
+		virtual void HandleCallbackReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc)=0;
+
+		bool should_cfi_pin(IRDB_SDK::Instruction_t* insn);
+
+		// workhorses 
+		void DoUnpin();
+		void DoUnpinForScoops();
+		void DoUnpinForFixedCalls();
+
+		void DoUpdate();
+		void DoUpdateForScoops();
+		void DoUpdateForInstructions();
+
+		Zipr_SDK::Zipr_t* zo;
+
+		Zipr_SDK::ZiprBooleanOption_t *m_verbose;
+		Zipr_SDK::ZiprBooleanOption_t *m_should_cfi_pin;
+		Zipr_SDK::ZiprBooleanOption_t *m_on; 
+		Zipr_SDK::ZiprIntegerOption_t *m_max_unpins; 
+
+		int unpins;
+		int missed_unpins=0;
+		Zipr_SDK::MemorySpace_t& ms;
+		Zipr_SDK::InstructionLocationMap_t& locMap;
+		IRDB_SDK::FileIR_t& firp;
+
+};
+
+
+class UnpinX86_t : public Unpin_t
+{
+	public:
+		UnpinX86_t( Zipr_SDK::Zipr_t* zipr_object) 
+			: Unpin_t(zipr_object)
+
+		{ 
+		}
+	protected:
+		// designed for arch-specific override.
+		void HandleRetAddrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandlePcrelReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleAbsptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleImmedptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleCallbackReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+
+
+};
+
+class UnpinAarch64_t : public Unpin_t
+{
+	public:
+		UnpinAarch64_t( Zipr_SDK::Zipr_t* zipr_object) 
+			: Unpin_t(zipr_object)
+
+		{ 
+		}
+	protected:
+		// designed for arch-specific override.
+		void HandleRetAddrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandlePcrelReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleAbsptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleImmedptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleCallbackReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+
+
+};
+
+class UnpinArm32_t : public Unpin_t
+{
+	public:
+		UnpinArm32_t( Zipr_SDK::Zipr_t* zipr_object) 
+			: Unpin_t(zipr_object)
+
+		{ 
+		}
+	protected:
+		// designed for arch-specific override.
+		void HandleRetAddrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandlePcrelReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleAbsptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleImmedptrReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+		void HandleCallbackReloc(IRDB_SDK::Instruction_t* from_insn,IRDB_SDK::Relocation_t* reloc) override;
+
+
+};
+
+class UnpinMips32_t : public Unpin_t
+{
+	public:
+		UnpinMips32_t( Zipr_SDK::Zipr_t* zipr_object) 
+			: Unpin_t(zipr_object)
+
+		{ 
+		}
+	protected:
+		// designed for arch-specific override.
+		void HandleRetAddrReloc (IRDB_SDK::Instruction_t* from_insn, IRDB_SDK::Relocation_t* reloc) override;
+		void HandlePcrelReloc   (IRDB_SDK::Instruction_t* from_insn, IRDB_SDK::Relocation_t* reloc) override;
+		void HandleAbsptrReloc  (IRDB_SDK::Instruction_t* from_insn, IRDB_SDK::Relocation_t* reloc) override;
+		void HandleImmedptrReloc(IRDB_SDK::Instruction_t* from_insn, IRDB_SDK::Relocation_t* reloc) override;
+		void HandleCallbackReloc(IRDB_SDK::Instruction_t* from_insn, IRDB_SDK::Relocation_t* reloc) override;
+
+
+};
+#endif
diff --git a/zipr_unpin_plugin/unpin_aarch64.cpp b/zipr_unpin_plugin/unpin_aarch64.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90fbe1d3061460a08423f992ce0bf913c4c440e5
--- /dev/null
+++ b/zipr_unpin_plugin/unpin_aarch64.cpp
@@ -0,0 +1,543 @@
+/***************************************************************************
+ * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
+ *
+ * This software is furnished under a license and/or other restrictive
+ * terms and may be used and copied only in accordance with such terms
+ * and the inclusion of the above copyright notice. This software or
+ * any other copies thereof may not be provided or otherwise made
+ * available to any other person without the express written consent
+ * of an authorized representative of Zephyr Software LCC. Title to,
+ * ownership of, and all rights in the software is retained by
+ * Zephyr Software LCC.
+ *
+ * Zephyr Software LLC. Proprietary Information
+ *
+ * Unless otherwise specified, the information contained in this
+ * directory, following this legend, and/or referenced herein is
+ * Zephyr Software LLC. (Zephyr) Proprietary Information.
+ *
+ * CONTACT
+ *
+ * For technical assistance, contact Zephyr Software LCC. at:
+ *
+ *
+ * Zephyr Software, LLC
+ * 2040 Tremont Rd
+ * Charlottesville, VA 22911
+ *
+ * E-mail: jwd@zephyr-software.com
+ **************************************************************************/
+
+
+#include <string>
+#include <algorithm>
+#include "unpin.h"
+#include <memory>
+#include <inttypes.h>
+
+
+using namespace IRDB_SDK;
+using namespace std;
+using namespace Zipr_SDK;
+
+
+#define ALLOF(a) begin(a),end(a)
+// per machine stuff
+void UnpinAarch64_t::HandleRetAddrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+
+void UnpinAarch64_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	// decode the instruction and find the pcrel operand
+	const auto disasm=DecodedInstruction_t::factory(from_insn);
+	const auto operands=disasm->getOperands();
+	const auto the_arg_it=find_if(ALLOF(operands),[](const shared_ptr<DecodedOperand_t>& op){ return op->isPcrel(); });
+	const auto bo_wrt=reloc->getWRT();
+	const auto scoop_wrt=dynamic_cast<DataScoop_t*>(reloc->getWRT());
+	const auto insn_wrt=dynamic_cast<Instruction_t*>(reloc->getWRT());
+	assert(the_arg_it!=operands.end());
+	const auto the_arg=*the_arg_it;
+
+	// get the new insn addr 	
+	const auto from_insn_location=(VirtualOffset_t)locMap[from_insn];
+
+	// get WRT info
+	IRDB_SDK::VirtualOffset_t to_addr=0xdeadbeef; // noteable value that shouldn't be used.
+	string convert_string;
+
+	if(scoop_wrt)
+	{
+		to_addr=scoop_wrt->getStart()->getVirtualOffset();
+		convert_string=string("scoop ")+scoop_wrt->getName();
+	}
+	else if(insn_wrt)
+	{
+		to_addr=locMap[insn_wrt];
+		convert_string=string("insn ")+to_string(insn_wrt->getBaseID())+
+			       ":"+insn_wrt->getDisassembly();
+	}
+	else 
+	{
+		assert(bo_wrt==nullptr);
+		to_addr=0; /* no WRT obj */
+		convert_string=string("no-object");
+	}
+
+
+	assert(bo_wrt==nullptr); // not yet imp'd WRT offsetting.
+	assert(to_addr==0); // not yet imp'd WRT offsetting.
+	const auto mnemonic        = disasm->getMnemonic();
+	const auto is_adr_type     = mnemonic=="adr";
+	const auto is_adrp_type    = mnemonic=="adrp";
+	const auto is_ldr_type     = mnemonic=="ldr";
+	const auto is_ldr_int_type = is_ldr_type && disasm->getOperand(0)->isGeneralPurposeRegister();
+	const auto is_ldr_fp_type  = is_ldr_type && disasm->getOperand(0)->isFpuRegister();
+	const auto is_ldrsw_type   = mnemonic=="ldrsw";
+	const auto mask1 =(1<< 1)-1;
+	const auto mask2 =(1<< 2)-1;
+	const auto mask5 =(1<< 5)-1;
+	const auto mask12=(1<<12)-1;
+	const auto mask19=(1<<19)-1;
+	const auto orig_insn_addr=from_insn->getAddress()->getVirtualOffset(); // original location
+	const auto insn_bytes_len=4;	// arm is always 4.
+	uint8_t insn_bytes[insn_bytes_len]; // compiler disallows init on some platforms.
+	// but memcpy should init it sufficiently.
+	memcpy(insn_bytes, from_insn->getDataBits().c_str(), insn_bytes_len);
+	auto full_insn=(uint32_t)0;
+	memcpy(&full_insn,insn_bytes, sizeof(full_insn));
+	const auto op_byte=insn_bytes[3];
+
+	if(is_adrp_type || is_adr_type)
+	{
+
+		// adr : 0 immlo2 10000 immhi19 Rd5
+		// adrp: 1 immlo2 10000 immhi19 Rd5
+		assert((op_byte&mask5) == 0x10); // sanity check adr(p) opcode bytes.
+		const auto immlo2    = (op_byte >> 5)&mask2; // grab immlo2
+		const auto immhi19   = (full_insn >> 5)&mask19; // grab immhi19
+		const auto imm21     = immhi19<<2 | immlo2;   // get full immediate in one field.
+		const auto imm21_ext = (((int64_t)imm21)<<43) >> 43; // sign extend to 64-bit
+
+		const auto shift_dist= 
+			is_adrp_type ? 12 : // bits in page for adrp 
+			is_adr_type  ?  0 : // no shift for adr
+			throw invalid_argument("Unknown adr insn");
+
+		const auto orig_insn_pageno = orig_insn_addr>>shift_dist;
+		const auto new_insn_pageno  = from_insn_location>>shift_dist;
+		const auto new_imm21_ext = imm21_ext + (int64_t)orig_insn_pageno - 
+				(int64_t)new_insn_pageno + (int64_t)reloc->getAddend()+(int64_t)to_addr;
+
+		// make sure no overflow.
+		if(((new_imm21_ext << 43) >> 43) == new_imm21_ext)
+		{
+			const auto new_immhi19   = new_imm21_ext >> 2;
+			const auto new_immlo2    = new_imm21_ext  & mask2;
+			const auto clean_new_insn= full_insn & ~(mask2<<29) & ~ (mask19 << 5);
+			const auto new_insn      = clean_new_insn | ((new_immlo2&mask2) << 29) | ((new_immhi19&mask19)<<5);
+			// put the new instruction in the output
+			ms.plopBytes(from_insn_location, (const char*)&new_insn, insn_bytes_len);
+			if (m_verbose)
+			{
+				cout << "Relocating a adr(p) pcrel relocation with orig_pageno=" << hex
+				     << (orig_insn_pageno << 12) << " offset=(page-pc+" << imm21_ext << ")"  << endl;
+				cout << "Based on: " << disasm->getDisassembly() << hex << " originally at "  << orig_insn_addr
+				     << " now located at : 0x" << hex << from_insn_location << " with offset=(page-pc + "
+				     << new_imm21_ext << ")" << endl;
+			}
+		}
+		else
+		{
+			assert(is_adr_type); // don't even know what to do if the PAGE is too far away!
+			// imm21->64 bit address didn't work.  Split it up into two parts.
+
+			/* the plan :
+			 * FA: b   L0
+			 * FT:
+			 * ..
+			 * L0  adrp dest_reg, <addr-page number>
+			 * L1  add dest_reg, dest_reg, (addr-page offset)
+			 * L2: b ft
+			 */
+			const auto tramp_size=3*4; // 3 insns, 4 bytes each
+			const auto address_to_generate=imm21_ext+orig_insn_addr+(int64_t)reloc->getAddend()+(int64_t)to_addr;
+			const auto destreg=full_insn&mask5;
+			const auto tramp_range=ms.getFreeRange(tramp_size);
+			const auto tramp_start=tramp_range.getStart();
+			// don't be too fancy, just reserve 12 bytes.
+			ms.splitFreeRange({tramp_start,tramp_start+12});
+
+
+			const auto FA=from_insn_location;
+			const auto FT=from_insn_location+4;
+			const auto L0=tramp_start;
+			const auto L1=tramp_start+4;
+			const auto L2=tramp_start+8;
+			const auto branch_bytes=string("\x00\x00\x00\x14",4);
+			// const auto updated_orig_insn_pageno = orig_insn_addr>>12; // orig_insn_pageno was shifted by 0 for adr
+			const auto relocd_insn_pageno  = L0>>12;
+			const auto address_to_generate_pageno = address_to_generate >> 12;
+			const auto address_to_generate_page_offset = address_to_generate & mask12;
+			const auto relocd_imm21_ext = (int64_t)address_to_generate_pageno - (int64_t)relocd_insn_pageno;
+			const auto relocd_immhi19   = relocd_imm21_ext >> 2;
+			const auto relocd_immlo2    = relocd_imm21_ext  & mask2;
+
+			// this should be +/- 4gb, so we shouldn't fail now!
+			assert(((relocd_imm21_ext << 43) >> 43) == relocd_imm21_ext);
+
+			// put an uncond branch at where the adr was.
+			// and make it point at L0
+			ms.plopBytes(FA,branch_bytes.c_str(),4);
+			zo->applyPatch(FA,L0);
+
+			// adrp: 1 imm2lo 1 0000 immhi19 Rd
+			auto adrp_bytes=string("\x00\x00\x00\x90",4);
+			auto adrp_word =*(int*)adrp_bytes.c_str();
+			adrp_word|=destreg<<0;
+			adrp_word |=  ((relocd_immlo2&mask2) << 29) | ((relocd_immhi19&mask19)<<5);
+			cout << "Tramp for "<<L0<<", relocd_immlo2=" << relocd_immlo2
+			     << ", relocd_immhi19=" << relocd_immhi19 << endl;
+			ms.plopBytes(L0,(char*)&adrp_word,4);
+
+			// add64 imm12 = 1001 0001 00 imm12 Rn Rd
+			auto add_bytes=string("\x00\x00\x00\x91",4);
+			auto add_word =*(int*)add_bytes.c_str();
+			add_word|=destreg<<0;
+			add_word|=destreg<<5;
+			add_word|=address_to_generate_page_offset << 10 ;
+			ms.plopBytes(L1,(char*)&add_word,4);
+
+			// put an uncond branch the end of the trampoline
+			// and make it jump at FT
+			ms.plopBytes(L2,branch_bytes.c_str(),4);
+			zo->applyPatch(L2,FT);
+
+			// should be few enough of these to always print
+			cout<< "Had to trampoline " << disasm->getDisassembly() << "@"<<FA<<" to "
+			    << hex << L0 << "-" << L0+tramp_size << endl;
+		}
+	}
+	else if(is_ldr_type)
+	{
+		// ldr w/x reg    : 0 x1 0110 0 0 imm19 Rt5, x1   indicate size (0,1 -> w/x) 
+		// ldr s/d/q reg  : opc2 0111 0 0 imm19 Rt5, opc2 indicate size (00,01,10 -> s/d/q)
+		const auto imm19    = ((int64_t)full_insn >> 5 ) & mask19;
+		const auto imm19_ext= (imm19 << 45) >> 45;
+		const auto referenced_addr=(imm19_ext<<2)+from_insn->getAddress()->getVirtualOffset()+4;
+		const auto new_imm19_ext  =((int64_t)referenced_addr-(int64_t)from_insn_location-4+(int64_t)reloc->getAddend()+(int64_t)to_addr)>>2;
+		if( ((new_imm19_ext << 45) >> 45) == new_imm19_ext)
+		{
+			const auto clean_new_insn = full_insn & ~(mask19 << 5);
+			const auto new_insn       = clean_new_insn | ((new_imm19_ext & mask19)<<5);
+			// put the new instruction in the output
+			ms.plopBytes(from_insn_location, (const char*)&new_insn, insn_bytes_len);
+			if (m_verbose)
+			{
+				cout << "Relocating a ldr pcrel relocation with orig_addr=" << hex
+				     << (referenced_addr) << " offset=(pc+" << imm19_ext << ")"  << endl;
+				cout << "Based on: " << disasm->getDisassembly() 
+				     << " now located at : 0x" << hex << from_insn_location << " with offset=(pc + "
+				     << new_imm19_ext << ")" << endl;
+			}
+		}
+		else
+		{
+			const auto address_to_generate=(imm19_ext<<2)+orig_insn_addr+(int64_t)reloc->getAddend()+(int64_t)to_addr;
+			const auto destreg=full_insn&mask5;
+			const auto FA=from_insn_location;
+			const auto FT=from_insn_location+4;
+			const auto branch_bytes=string("\x00\x00\x00\x14",4);
+			const auto address_to_generate_pageno = address_to_generate >> 12;
+			const auto address_to_generate_page_offset = address_to_generate & mask12;
+
+			if(is_ldr_int_type)
+			{
+				// imm19->64 bit address didn't work.  Split it up into two parts.
+
+				/* the plan :
+				 * FA: b   L0
+				 * FT:
+				 * ..
+				 * L0  adrp dest_reg, <addr-page number>
+				 * L1  ldr dest_reg, [dst_reg, #addr-page offset]
+				 * L2: b ft
+				 */
+				const auto tramp_size=3*4; // 3 insns, 4 bytes each
+				const auto tramp_range=ms.getFreeRange(tramp_size);
+				const auto tramp_start=tramp_range.getStart();
+				// don't be too fancy, just reserve 12 bytes.
+				ms.splitFreeRange({tramp_start,tramp_start+12});
+
+
+				// and give the bytes some names
+				const auto L0=tramp_start;
+				const auto L1=tramp_start+4;
+				const auto L2=tramp_start+8;
+
+				// calculate the immediates for the new adr and ldr instruction.
+				const auto relocd_insn_pageno  = L1>>12;
+				const auto relocd_imm21_ext = (int64_t)address_to_generate_pageno - (int64_t)relocd_insn_pageno;
+				const auto relocd_immhi19   = relocd_imm21_ext >> 2;
+				const auto relocd_immlo2    = relocd_imm21_ext  & mask2;
+				// this should be +/- 4gb, so we shouldn't fail now!
+				assert(((relocd_imm21_ext << 43) >> 43) == relocd_imm21_ext);
+
+
+				// put an uncond branch at where the adr was.
+				// and make it point at L0
+				ms.plopBytes(FA,branch_bytes.c_str(),4);
+				zo->applyPatch(FA,L0);
+
+				// adrp: 1 imm2lo 1 0000 immhi19 Rd
+				auto adrp_bytes=string("\x00\x00\x00\x90",4);
+				auto adrp_word =*(int*)adrp_bytes.c_str();
+				adrp_word|=destreg<<0;
+				adrp_word |=  ((relocd_immlo2&mask2) << 29) | ((relocd_immhi19&mask19)<<5);
+				ms.plopBytes(L0,(char*)&adrp_word,4);
+
+				// convert: ldr w/x reg : 0 x1 011 0 00 ---imm19---- Rt5    x1 indicates size (0,1 -> w/x) 
+				// to     : ldr x/w reg : 1 x1 111 0 01 01 imm12 Rn5 Rt5    x1 indciates size (0,1 -> w/x)
+				auto new_ldr_bytes=string("\x00\x00\x40\xb9",4);
+				auto new_ldr_word =*(int*)new_ldr_bytes.c_str();
+				const auto orig_ldr_size_bit=(full_insn>>30)&mask1;
+				const auto scale=0x2|orig_ldr_size_bit;
+				const auto scaled_page_offset=(address_to_generate_page_offset>>scale) ;
+				new_ldr_word|=destreg<<0; // Rt
+				new_ldr_word|=destreg<<5; // Rn
+				new_ldr_word|=scaled_page_offset << 10 ; // imm12
+				new_ldr_word|=orig_ldr_size_bit << 30; // x1
+				ms.plopBytes(L1,(char*)&new_ldr_word,4);
+
+				// put an uncond branch the end of the trampoline
+				// and make it jump at FT
+				ms.plopBytes(L2,branch_bytes.c_str(),4);
+				zo->applyPatch(L2,FT);
+
+				// 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_fp_type)
+			{
+				/* the scheme for int-type operations doesn't work
+				 * for fp-type because  there is no free register.
+				 * use this plan:
+				 * FA: b   L0
+				 * FT:
+				 * ..
+				 * L0  str x0, [sp+128] ; 128 for red zoning
+				 * L1  adrp x0, <addr-page number>
+				 * L2  ldr dest_reg, [x0, #addr-page offset]
+				 * L3  ldr x0, [sp+128] ; 128 for red zoning
+				 * L4  b FT
+				 */
+
+
+
+				// allocate and reserve space for the code.
+				const auto tramp_size=5*4; // 3 insns, 4 bytes each
+				const auto tramp_range=ms.getFreeRange(tramp_size);
+				const auto tramp_start=tramp_range.getStart();
+				// don't be too fancy, just reserve 12 bytes.
+				ms.splitFreeRange({tramp_start,tramp_start+12});
+
+
+				// give the bytes some names
+				const auto L0=tramp_start   ;
+				const auto L1=tramp_start+4 ;
+				const auto L2=tramp_start+8 ;
+				const auto L3=tramp_start+12;
+				const auto L4=tramp_start+16;
+
+				// calculate the immediates for the new adr and ldr instruction.
+				const auto relocd_insn_pageno  = L1>>12;
+				const auto relocd_imm21_ext    = (int64_t)address_to_generate_pageno - (int64_t)relocd_insn_pageno;
+				const auto relocd_immhi19      = relocd_imm21_ext >> 2;
+				const auto relocd_immlo2       = relocd_imm21_ext  & mask2;
+				// this should be +/- 4gb, so we shouldn't fail now!
+				assert(((relocd_imm21_ext << 43) >> 43) == relocd_imm21_ext);
+
+				// put an uncond branch at where the adr was.
+				// and make it point at L0
+				ms.plopBytes(FA,branch_bytes.c_str(),4);
+				zo->applyPatch(FA,L0);
+
+				// put save of x0 in place.
+				// diassembly: f81803e0        stur    x0, [sp, #-128]
+				const auto strx0_bytes=string("\xe0\x03\x18\xf8",4);
+				ms.plopBytes(L0,strx0_bytes.c_str(),4);
+
+				// adrp: 1 imm2lo 1 0000 immhi19 Rd
+				auto adrp_bytes=string("\x00\x00\x00\x90",4);
+				auto adrp_word =*(int*)adrp_bytes.c_str();
+				// adrp_word|=destreg<<0; ; destreg for this insn is x0.
+				adrp_word |=  ((relocd_immlo2&mask2) << 29) | ((relocd_immhi19&mask19)<<5);
+				ms.plopBytes(L1,(char*)&adrp_word,4);
+
+
+				// convert: ldr   s/d/q reg: opc2  01 11 00 imm19 Rt5, opc2 indicate size (00,01,10 -> s/d/q)
+				// to:      ldr b/s/d/q reg: size2 11 11 01 opc2 imm12 Rn Rt
+				auto new_ldr_bytes=string("\x00\x00\x00\x3d",4);
+				auto new_ldr_word =*(int*)new_ldr_bytes.c_str();
+				const auto orig_ldr_opc_bits=(full_insn>>30)&mask2;
+
+				// decode size out of old ldr
+				const auto ldr_size= 
+					orig_ldr_opc_bits == 0x0 ? 4u  :
+					orig_ldr_opc_bits == 0x1 ? 8u  :
+					orig_ldr_opc_bits == 0x2 ? 16u :
+					throw invalid_argument("cannot decode ldr floating-point access size");
+
+				// encode size field for new ldr.
+				const auto new_ldr_size_bits=
+					ldr_size == 4  ? 0x2u :
+					ldr_size == 8  ? 0x3u :
+					ldr_size == 16 ? 0x0u :
+					throw invalid_argument("cannot decode ldr floating-point access size");
+
+				// encode opc2
+				const auto new_ldr_opc2_bits=
+					ldr_size == 4  ? 0x1u :
+					ldr_size == 8  ? 0x1u :
+					ldr_size == 16 ? 0x3u :
+					throw invalid_argument("cannot decode ldr floating-point access size");
+
+				// add variable fields to new insn and drop it in the mem space.
+				new_ldr_word|=destreg<<0; // Rt -- should be actual dest reg, not x0
+				// new_ldr_word|=destreg<<5; // Rn -- should be x0
+				new_ldr_word|=((address_to_generate_page_offset/ldr_size) << 10); // imm12
+				new_ldr_word|=(new_ldr_size_bits<<30); // size2
+				new_ldr_word|=(new_ldr_opc2_bits<<22); // opc2
+				ms.plopBytes(L2,(char*)&new_ldr_word,4);
+
+
+				// drop in the restore of x0
+				// disassembly:   f85803e0        ldur    x0, [sp, #-128]
+				const auto ldrx0_bytes=string("\xe0\x03\x58\xf8",4);
+				ms.plopBytes(L3,ldrx0_bytes.c_str(),4);
+
+				// put an uncond branch the end of the trampoline
+				// and make it jump at FT
+				ms.plopBytes(L4,branch_bytes.c_str(),4);
+				zo->applyPatch(L4,FT);
+
+				// 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
+				assert(0);
+		}
+
+	}
+	else if(is_ldrsw_type)
+	{
+		// ldrsw x reg    : 1001 1000 imm19 Rt
+		const auto imm19    = ((int64_t)full_insn >> 5 ) & mask19;
+		const auto imm19_ext= (imm19 << 45) >> 45;
+		const auto referenced_addr=(imm19_ext<<2)+from_insn->getAddress()->getVirtualOffset()+4;
+		const auto new_imm19_ext  =((int64_t)referenced_addr-(int64_t)from_insn_location-4+(int64_t)reloc->getAddend()+(int64_t)to_addr)>>2;
+		if( ((new_imm19_ext << 45) >> 45) == new_imm19_ext)
+		{
+			const auto clean_new_insn = full_insn & ~(mask19 << 5);
+			const auto new_insn       = clean_new_insn | ((new_imm19_ext & mask19)<<5);
+			// put the new instruction in the output
+			ms.plopBytes(from_insn_location, (const char*)&new_insn, insn_bytes_len);
+			if (m_verbose)
+			{
+				cout << "Relocating a ldrsw pcrel relocation with orig_addr=" << hex
+				     << (referenced_addr) << " offset=(pc+" << imm19_ext << ")"  << endl;
+				cout << "Based on: " << disasm->getDisassembly() 
+				     << " now located at : 0x" << hex << from_insn_location << " with offset=(pc + "
+				     << new_imm19_ext << ")" << endl;
+			}
+		}
+		else
+		{
+			// imm19->64 bit address didn't work.  Split it up into two parts.
+
+			/* the plan :
+			 * FA: b   L0
+			 * FT:
+			 * ..
+			 * L0  adrp dest_reg, <addr-page number>
+			 * L1  ldr dest_reg, [dst_reg, #addr-page offset]
+			 * L2: b ft
+			 */
+			const auto tramp_size=3*4; // 3 insns, 4 bytes each
+			const auto address_to_generate=(imm19_ext<<2)+orig_insn_addr+(int64_t)reloc->getAddend()+(int64_t)to_addr;
+			const auto destreg=full_insn&mask5;
+			const auto tramp_range=ms.getFreeRange(tramp_size);
+			const auto tramp_start=tramp_range.getStart();
+			// don't be too fancy, just reserve 12 bytes.
+			ms.splitFreeRange({tramp_start,tramp_start+12});
+
+
+			const auto FA=from_insn_location;
+			const auto FT=from_insn_location+4;
+			const auto L0=tramp_start;
+			const auto L1=tramp_start+4;
+			const auto L2=tramp_start+8;
+			const auto branch_bytes=string("\x00\x00\x00\x14",4);
+			const auto relocd_insn_pageno  = L1>>12;
+			const auto address_to_generate_pageno = address_to_generate >> 12;
+			const auto address_to_generate_page_offset = address_to_generate & mask12;
+			const auto relocd_imm21_ext = (int64_t)address_to_generate_pageno - (int64_t)relocd_insn_pageno;
+			const auto relocd_immhi19   = relocd_imm21_ext >> 2;
+			const auto relocd_immlo2    = relocd_imm21_ext  & mask2;
+
+			// this should be +/- 4gb, so we shouldn't fail now!
+			assert(((relocd_imm21_ext << 43) >> 43) == relocd_imm21_ext);
+
+			// put an uncond branch at where the adr was.
+			// and make it point at L0
+			ms.plopBytes(FA,branch_bytes.c_str(),4);
+			zo->applyPatch(FA,L0);
+
+			// adrp: 1 imm2lo 1 0000 immhi19 Rd
+			auto adrp_bytes=string("\x00\x00\x00\x90",4);
+			auto adrp_word =*(int*)adrp_bytes.c_str();
+			adrp_word|=destreg<<0;
+			adrp_word |=  ((relocd_immlo2&mask2) << 29) | ((relocd_immhi19&mask19)<<5);
+			ms.plopBytes(L0,(char*)&adrp_word,4);
+
+			// convert: ldrsw x reg : 1001 1000 ---imm19--- Rt
+			// to     : ldrsw x reg : 1011 1001 10 imm12 Rn Rt
+			auto new_ldr_bytes=string("\x00\x00\x80\xb9",4);
+			auto new_ldr_word =*(int*)new_ldr_bytes.c_str();
+			const auto scale=0x2;
+			const auto scaled_page_offset=(address_to_generate_page_offset>>scale) ;
+			new_ldr_word|=destreg<<0; // Rt
+			new_ldr_word|=destreg<<5; // Rn
+			new_ldr_word|=scaled_page_offset << 10 ; // imm12
+			ms.plopBytes(L1,(char*)&new_ldr_word,4);
+
+			// put an uncond branch the end of the trampoline
+			// and make it jump at FT
+			ms.plopBytes(L2,branch_bytes.c_str(),4);
+			zo->applyPatch(L2,FT);
+
+			// should be few enough of these to always print
+			cout<< "Had to trampoline " << disasm->getDisassembly() << "@"<<FA<<" to "
+			    << hex << L0 << "-" << L0+tramp_size-1 << endl;
+		}
+	}
+}
+
+
+void UnpinAarch64_t::HandleAbsptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); } 
+
+
+void UnpinAarch64_t::HandleImmedptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+void UnpinAarch64_t::HandleCallbackReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+
+
+
diff --git a/zipr_unpin_plugin/unpin_arm32.cpp b/zipr_unpin_plugin/unpin_arm32.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe643c493c895f1a0436475500bbbb465fdf765c
--- /dev/null
+++ b/zipr_unpin_plugin/unpin_arm32.cpp
@@ -0,0 +1,686 @@
+/***************************************************************************
+ * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
+ *
+ * This software is furnished under a license and/or other restrictive
+ * terms and may be used and copied only in accordance with such terms
+ * and the inclusion of the above copyright notice. This software or
+ * any other copies thereof may not be provided or otherwise made
+ * available to any other person without the express written consent
+ * of an authorized representative of Zephyr Software LCC. Title to,
+ * ownership of, and all rights in the software is retained by
+ * Zephyr Software LCC.
+ *
+ * Zephyr Software LLC. Proprietary Information
+ *
+ * Unless otherwise specified, the information contained in this
+ * directory, following this legend, and/or referenced herein is
+ * Zephyr Software LLC. (Zephyr) Proprietary Information.
+ *
+ * CONTACT
+ *
+ * For technical assistance, contact Zephyr Software LCC. at:
+ *
+ *
+ * Zephyr Software, LLC
+ * 2040 Tremont Rd
+ * Charlottesville, VA 22911
+ *
+ * E-mail: jwd@zephyr-software.com
+ **************************************************************************/
+
+
+#include <string>
+#include <algorithm>
+#include "unpin.h"
+#include <memory>
+#include <inttypes.h>
+#include <stdint.h>
+#include <limits.h>
+#include <irdb-util>
+
+
+using namespace IRDB_SDK;
+using namespace std;
+using namespace Zipr_SDK;
+
+static inline uint32_t rotr32 (uint32_t n, unsigned int c)
+{
+  const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);
+
+  // assert ( (c<=mask) &&"rotate by type width or more");
+  c &= mask;
+  return (n>>c) | (n<<( (-c)&mask ));
+}
+
+
+#define ALLOF(a) begin(a),end(a)
+// per machine stuff
+void UnpinArm32_t::HandleRetAddrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+
+void UnpinArm32_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	// decode the instruction and find the pcrel operand
+	const auto disasm         = DecodedInstruction_t::factory(from_insn);
+	const auto mnemonic       = disasm->getMnemonic();
+	const auto orig_insn_addr = from_insn->getAddress()->getVirtualOffset(); // original location
+	const auto insn_bytes_len = 4;	// arm is always 4.
+	const auto bo_wrt         = reloc->getWRT();
+	const auto scoop_wrt      = dynamic_cast<DataScoop_t* >(reloc->getWRT());
+	const auto insn_wrt       = dynamic_cast<Instruction_t*>(reloc->getWRT());
+	const auto branch_bytes   =  string("\x00\x00\x00\xea",4);
+	uint8_t    insn_bytes[insn_bytes_len]; // compiler disallows init on some platforms.
+        // but memcpy should init it sufficiently.
+        memcpy(insn_bytes, from_insn->getDataBits().c_str(), insn_bytes_len);
+        auto full_insn=(uint32_t)0;
+	memcpy(&full_insn,insn_bytes, sizeof(full_insn));
+
+
+	const auto mask1  = (1<<1 )-1;
+	const auto mask4  = (1<<4 )-1;
+	const auto mask8  = (1<<8 )-1;
+	const auto mask12 = (1<<12)-1;
+	const auto allocate_reg = [](const set<uint32_t>& used) -> uint32_t
+		{
+			for(auto i=0u; i<15; i++)
+			{
+				if(used.find(i) == end(used))
+					return i;
+			}
+			assert(0);
+		};
+
+	// get the new insn addr 	
+	const auto from_insn_location = VirtualOffset_t(locMap[from_insn]);
+
+	// get WRT info
+	const auto to_addr = 
+		(scoop_wrt != nullptr) ?  scoop_wrt->getStart()->getVirtualOffset() : // is scoop
+		(insn_wrt  != nullptr) ?  locMap[insn_wrt]                          : // is instruction
+		(bo_wrt    == nullptr) ?  VirtualOffset_t(0u)                       : // no WRT obj 
+		throw invalid_argument("Cannot map pcrel relocation WRT object to address");
+	const auto addend       = reloc -> getAddend(); 
+	const auto reloc_offset = to_addr + addend;
+
+	const auto to_object_id =  
+		(scoop_wrt != nullptr) ?  scoop_wrt->getName()       +"@"+to_hex_string(scoop_wrt->getStart()  ->getVirtualOffset()) : // scoop 
+		(insn_wrt  != nullptr) ?  insn_wrt ->getDisassembly()+"@"+to_hex_string(insn_wrt ->getAddress()->getVirtualOffset()) : // instruction
+		(bo_wrt    == nullptr) ?  string("No-object") : 
+		throw invalid_argument("Cannot map pcrel relocation WRT object to address");
+
+
+	// 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
+	const auto is_ldrls_type = mnemonic == "ldrls";                 // exactly ldrls
+	const auto I_bit_set     = 0b1 == ( (full_insn >> 25) & mask1);	// 25th bit indicates if op2 is shifted immediate.  
+	const auto S_bit_set     = 0b1 == ( (full_insn >> 20) & mask1);	// 20th bit indicates if the flags are updated.  not valid for all insns.
+	const auto Rd            = uint32_t(full_insn >> 12) & mask4;
+	const auto Rm            = uint32_t(full_insn >>  0) & mask4;
+	const auto Rn            = uint32_t(full_insn >> 16) & mask4;
+	const auto Rs            = uint32_t(full_insn >>  8) & mask4;
+	const auto is_rn_pc      = Rn == 0b1111;                        // rn reg may not be valid for all instructions
+	const auto is_rd_pc      = Rd == 0b1111;	                // destination register, not valid for all insns 
+	const auto is_pos_imm   = (bool)((full_insn >> 23) & mask1);	// useful only for ldr and vldr
+
+	// find a temp_reg if we need one
+	const auto tmp_reg = allocate_reg({Rd,Rm,Rn,Rs, 13, 15});
+	assert(tmp_reg < 15); // sanity check 4 bits
+
+
+	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 L6
+		const auto ldr_imm_field = int32_t(full_insn & mask8)*4;
+		const auto ldr_imm       = is_pos_imm ? ldr_imm_field : -ldr_imm_field;
+		const auto new_offset    = (bo_wrt == nullptr)   ?
+			int32_t(orig_insn_addr - L2 + addend) :
+			int32_t(orig_insn_addr - (L2 + 8) + to_addr + addend - ldr_imm );
+		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 << " WRT=" << to_object_id 
+		    << " ldr_imm = " << dec << ldr_imm 
+		    << endl;
+
+	}
+	else if( is_ldr_type && !is_rd_pc && !I_bit_set)	/* ldr <not pc>, [pc, imm] */
+	{
+		/* 
+		 * We need to patch an ldr[b][cond] reg, [pc + constant]
+		 * to be at a new location.
+		 *
+		 * The plan :
+		 * FA: b<cond> L0
+		 * FT:
+		 * ..
+		 * L0  ldr     rd, [L3]
+		 * L1  ldr(b)  rd, [pc, rd]
+		 * L2: b       FT
+		 * L3: .word <constant to add>
+		 */
+		const auto tramp_size  = 4*4; // 3 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 = tramp_start + 4;
+		const auto L2 = tramp_start + 8;
+		const auto L3 = tramp_start + 12;
+
+		const auto is_byte_load = (bool)((full_insn >> 22) & mask1);
+
+		assert(Rd!=0xf);	 // not the program counter.
+
+		// 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);
+		ms.plopBytes(FA,my_branch_bytes.c_str(),4);
+		// and make it point at L0
+		zo->applyPatch(FA,L0);
+
+		// ldr dest_reg, [pc+k] (where pc+k == L3)
+		auto ldr_imm_insn = string("\x04\x00\x9f\xe5",4);
+		ldr_imm_insn[1]  |= (Rd << 4); // set this instruction's dest reg to the ldr's dest reg.
+		ms.plopBytes(L0,ldr_imm_insn.c_str(),4);
+
+		// create the modified ldr(b) from the original ldr instruction
+		auto new_ldr_word = string("\x00\x00\x9f\xe7",4);
+		new_ldr_word[1]  |= (Rd << 4);   // set this instruction's dest reg to the ldr's dest reg.
+		new_ldr_word[0]  |= Rd;          // set this instruction's 2nd src reg to the orig ldr's dest reg.
+		new_ldr_word[3]  |= is_byte_load << 6; // set this instruction's B flag to match orig insn's
+		ms.plopBytes(L1,new_ldr_word.c_str(),4);
+
+		// put an uncond branch the end of the trampoline
+		// and make it jump at FT
+		ms.plopBytes(L2,branch_bytes.c_str(),4);
+		zo->applyPatch(L2,FT);
+
+		// put the calculated pc-rel offset at L3
+		const auto ldr_imm_field = int32_t(full_insn & mask12);
+		const auto ldr_imm       = is_pos_imm ? ldr_imm_field : - ldr_imm_field;
+		const auto new_addend    = bo_wrt == nullptr ?  8 + ldr_imm : reloc_offset;
+		const auto new_offset    = int32_t(orig_insn_addr - L3 + new_addend);
+		ms.plopBytes(L3,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 << " ldr_imm = " << ldr_imm << " WRT=" << to_object_id << endl;
+	}
+	else if( is_ldr_type && !is_rd_pc && I_bit_set)	/* ldr <not pc>, [pc, reg/shift] */
+	{
+		/* 
+		 * We need to patch a ldr Rd [pc, Rm <shift type> <shift amt>]@FA
+		 * to be at a new location.
+		 *
+		 * The plan:
+		 * FA: bne L0
+		 * FT:
+		 * ..
+		 * L0:  str Rt, [sp, # - fc]       # spill tmp reg (Rt), use tmp_reg instead of r0
+		 * L1:  ldr Rt, [pc, #k]           # where L1+8+k == L6 or k = L6-L1-8
+		 * L2:  add Rt, pc, Rt             # add in pc
+		 * L3:  ldr Rd, [Rt, Rm <shift type> <shift smt> ] 
+		 *                                 # copy of orig insn with pc (Rn field) replaced with Rt.
+		 * L4:  ldr Rt, [sp, # - fc]       # spill tmp reg (Rt), use tmp_reg instead of r0
+		 * L5:  b FT
+		 * L6:  .word L2 - orig_insn_addr
+		 */
+		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 tramp_size bytes.
+		ms.splitFreeRange({tramp_start,tramp_start+tramp_size});
+
+		// and give the bytes some names
+		const auto FA = from_insn_location;
+		const auto FT = FA + 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);
+		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);
+		ms.plopBytes(L1,ldr_imm_insn.c_str(),4);
+
+		// put down L2
+		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 L3 (orig insn with pc fields set to r0)
+		auto orig_ldr = from_insn->getDataBits();
+		orig_ldr[3]  &=  0b00001111;   // clear the cond bits.
+		orig_ldr[3]  |=  0b11100000;   // set the cond bits to "always".
+		orig_ldr[2]  &= ~(mask4 << 0); // clear this instruction's Rn field (i.e., set to r0)
+		orig_ldr[2]  |= (tmp_reg<<0); // set Rn fields o tmp_reg
+		ms.plopBytes(L3,orig_ldr.c_str(),4);
+
+		// put down L4, 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 L6
+		const auto new_offset = int32_t(orig_insn_addr - L2 + reloc_offset);
+		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() << " @" << hex << FA 
+		     << " to " << L0 << "-" << L0+tramp_size-1 << " WRT=" << to_object_id << endl;
+
+	}
+	else if((is_ldrls_type || is_addne_type || is_addls_type) && is_rd_pc && is_rn_pc)
+	{
+		/* 
+		 * We need to patch an addne pc, pc, reg lsl #2
+		 * to be at a new location.
+		 *
+		 * The plan :
+		 * FA: bne L0
+		 * FT:
+		 * ..
+		 * L0:  str r0, [sp, # - fc]       # spill tmp reg, use tmp_reg instead of r0
+		 * L1:  ldr r0, [pc, #k]           # where L1+8+k == L7 or k = L7-L1-8
+		 * L2:  add r0, pc, r0             # add in pc
+		 * L3:  op r0, r0, ( r2 lsl #2 )  # copy of orig insn with pc removed from op0 and op1, and replaced with tmp_reg.
+		 * L4:  str r0, [sp, # - f8]       # store calculated pc
+		 * L5:  ldr r0, [sp, # - fc]       # restore reg
+		 * L6:  ldr pc, [sp, # - f8]       # jmp dispatch
+		 * L7:  .word L2 - orig_insn_addr
+
+		 */
+		const auto tramp_size  = 7*4 + 4 ; // 7 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 tramp_size bytes.
+		ms.splitFreeRange({tramp_start,tramp_start+tramp_size});
+
+		// and give the bytes some names
+		const auto FA=from_insn_location;
+		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;
+		const auto L7 = L6 + 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);
+		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 == L7)
+		auto ldr_imm_insn = string("\x10\x00\x9f\xe5",4);
+		ldr_imm_insn[1]  |= (tmp_reg<<4);
+		ms.plopBytes(L1,ldr_imm_insn.c_str(),4);
+
+		// put down L2
+		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 L3 (orig insn with pc fields set to r0)
+		auto orig_add = from_insn->getDataBits();
+		orig_add[3]  &=  0b00001111;   // clear the cond bits.
+		orig_add[3]  |=  0b11100000;   // set the cond bits to "always".
+		orig_add[1]  &= ~(mask4 << 4); // clear this instruction's Rd field (i.e., set to r0)
+		orig_add[2]  &= ~(mask4 << 0); // clear this instruction's Rn field (i.e., set to r0)
+		orig_add[1]  |= (tmp_reg<<4); // set Rd and Rn fields to tmp_reg
+		orig_add[2]  |= (tmp_reg<<0);
+		ms.plopBytes(L3,orig_add.c_str(),4);
+
+		// put down L4, store of calc'd pc.
+		auto  spill_targ_insn = string("\xf8\x00\x0d\xe5",4);
+		spill_targ_insn[1]   |= (tmp_reg<<4); // set Rd field
+		ms.plopBytes(L4,spill_targ_insn.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(L5,restore_insn.c_str(),4);
+
+		// put down L6, the actual control transfer using the saved value on the stack
+		auto  xfer_insn = string("\xf8\x00\x1d\xe5",4);
+		xfer_insn[1]   |= (0b1111 << 4); // set this instruction's Rd reg to PC
+		ms.plopBytes(L6,xfer_insn.c_str(),4);
+
+		// put the calculated pc-rel offset at L7
+		const auto new_offset = int32_t(orig_insn_addr - L2 + reloc_offset);
+		ms.plopBytes(L7,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 << " WRT=" << to_object_id << endl;
+	}
+	else if(is_add_type && I_bit_set)
+	{
+		/* 
+		 *
+		 * here we've found a add<s><cond> Rd, Rn, #constant<<(#shift * 2)
+		 * e.g.: add ip, pc, #0, #12  # IP=PC+8+(12<<(0*2)) 
+		 *
+		 * the plan :
+		 * FA: b<cond> L0
+		 * FT:
+		 * ..
+		 * L0  ldr dest_reg, [L3]
+		 * L1  add dest_reg, pc, dest_reg
+		 * L2: b   FT
+		 * L3: .word <constant to add>
+		 */
+
+		assert(Rd!=0xf);	 // not the program counter.  needs to be handled as IB
+
+		const auto tramp_size  = 4*4; // 3 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 = tramp_start + 4;
+		const auto L2 = tramp_start + 8;
+		const auto L3 = tramp_start + 12;
+
+		// Create a branch to put over the original insn 
+		// 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);
+		ms.plopBytes(FA,my_branch_bytes.c_str(),4);
+		// and make it point at L0
+		zo->applyPatch(FA,L0);
+
+		// ldr dest_reg, [pc+k] (where pc+k == L3)
+		auto ldr_imm_insn = string("\x04\x00\x9f\xe5",4);
+		ldr_imm_insn[1]  |= (Rd << 4); // set this instruction's dest reg to the ldr's dest reg.
+		ms.plopBytes(L0,ldr_imm_insn.c_str(),4);
+
+		// create the modified add from the original ldr instruction
+		auto new_add_word = string("\x00\x00\x8f\xe0",4);   // e08f0000	 add r0, pc, r0
+		new_add_word[1]  |= (Rd << 4);   // set this instruction's dest reg to the origin insn's dest reg.
+		new_add_word[0]  |= Rd;          // set this instruction's 2nd src reg to the orig insn's dest reg.
+		new_add_word[3]  |= S_bit_set << 4; // set this instruction's S flag to match the orig insn
+		ms.plopBytes(L1,new_add_word.c_str(),4);
+
+		// put an uncond branch the end of the trampoline
+		// and make it jump at FT
+		ms.plopBytes(L2,branch_bytes.c_str(),4);
+		zo->applyPatch(L2,FT);
+
+		// put the calculated pc-rel offset at L3
+		const auto add_imm_field = int32_t(full_insn & mask8);
+		const auto add_ror_field = int32_t((full_insn>>8) & mask4);
+		const auto add_imm       = rotr32(add_imm_field,add_ror_field*2);
+		const auto orig_target   = orig_insn_addr + 8 +  add_imm;
+		const auto new_offset    = int32_t(orig_target - L3 + reloc_offset);
+		ms.plopBytes(L3,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 << " add_imm = " << add_imm << " WRT=" << to_object_id << endl;
+	}
+	else if(is_add_type && !I_bit_set)
+	{
+		/* 
+		 * here we've found a add<s><cond> Rd, Rn, Rm <shift_oper> ( shift * 2 )
+		 * e.g.  add Rd, pc, Rn          # Rd = pc + Rn
+		 * e.g2. add Rd, Rm, pc          # Rd = Rm + pc 
+		 * e.g3. add Rd, pc, Rn ror #2   # Rd = pc + (Rn ror #2)
+		 * e.g4. add Rd, Rm, pc lsl #2   # Rd = Rm + pc << 2
+		 * e.g5. add Rd, pc, Rn lsl Rs   # Rd = pc + (Rn << Rs)
+		 * e.g6. add Rd, Rm, pc lsl Rs   # Rd = Rm + (pc << Rs)
+		 *
+		 * Note: instruction may have a <cond> or an <s> appended
+		 * Note: Rd, Rn, and Rs may all be the same register.  We could optimize if they aren't, TBD.
+		 *
+		 * Assume: no shifting of pc (as in example 4 and 6.)  
+		 * Assume: no pc in Rn position (as in example 2, 4, and 6)
+		 * Assume: no pc in Rs position (as in example 6)
+		 *
+		 * Assume: tmp_reg is a (live) register that's not Rd, Rn, Rs, or pc.
+		 *
+		 *
+		 *
+		 * FA: b<cond> L0  # deals with all <cond> properly.
+		 * FT:
+		 * ..
+		 * L0  str    tmp_reg -> [sp - 0xfc] # save reg
+		 * L1  add    Rd, ...		     # orig add instruction, without <cond> or <s>
+		 * L2  ldr    tmp_reg <- [L3]        # load constant offset from cur pc to other pc
+		 * L3  add<s> Rd, tmp_reg, Rd        # add in constant, and set S flag.
+		 * L4  ldr    tmp_reg <- [sp - 0xfc] # restore reg
+		 * L5: b      FT 
+		 * L6: .word <orig_pc - new_pc> # Note:  all other fields factor out!
+		 */
+		assert(Rd!=0xf); // not the program counter.  needs to be handled as IB
+
+		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 the 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 insn 
+		// 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);	// set cond bits
+		ms.plopBytes(FA,my_branch_bytes.c_str(),4);
+		// and make it point at L0
+		zo->applyPatch(FA,L0);
+
+		// put down L0
+		auto  spill_insn = string("\xfc\x00\x0d\xe5",4);
+		spill_insn[1]   |= (tmp_reg << 4); // set this instruction's Rd reg to the tmp reg 
+		ms.plopBytes(L0,spill_insn.c_str(),4);
+
+		// put down L1
+		auto orig_add = from_insn->getDataBits();
+		orig_add[2]  &= ~(1 << 4); // clear the S bit.
+		orig_add[3]  &=  0b00001111;   // clear the cond bits.
+		orig_add[3]  |=  0b11100000;   // set the cond bits to "always".
+		ms.plopBytes(L1,orig_add.c_str(),4);
+
+		// Put down L2 (ldr dest_reg, [pc+k] where pc+k == L6)
+		auto ldr_imm_insn = string("\x08\x00\x9f\xe5",4);
+		ldr_imm_insn[1]  |= (tmp_reg << 4); // set this instruction's Rd to tmp_reg
+		ms.plopBytes(L2,ldr_imm_insn.c_str(),4);
+
+		// put down L3,   an add Rd, tmp_reg, Rd (e0800000) where Rd comes from the origin insn.
+		auto L3_insn = string("\x00\x00\x80\xe0",4);
+		L3_insn[1]  |= (Rd      << 4); // set this instruction's Rd field to orig insn's Rd 
+		L3_insn[2]  |= (tmp_reg << 0); // set this instruction's Rn field to the tmp reg 
+		L3_insn[0]  |= (Rd      << 0); // set this instruction's Rm field to orig insn's Rd
+		ms.plopBytes(L3,L3_insn.c_str(),4);
+
+		// put down L4, e59d00fc
+		auto  restore_insn = string("\xfc\x00\x1d\xe5",4);
+		restore_insn[1]   |= (tmp_reg << 4); // set this instruction's Rd reg to the tmp reg 
+		ms.plopBytes(L4,restore_insn.c_str(),4);
+
+		// put down L5, 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 - L1 + reloc_offset);
+		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 << " WRT=" << to_object_id << endl;
+	}
+	else 
+	{
+		cout <<"WARN: insn patching help: "<< from_insn->getDisassembly()<<endl;
+	}
+/*
+ * other instructions are probably false positives in the disassembly process.  let's just not patch them
+	else 
+		assert(0);
+*/
+
+}
+
+
+void UnpinArm32_t::HandleAbsptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); } 
+
+
+void UnpinArm32_t::HandleImmedptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+void UnpinArm32_t::HandleCallbackReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+
+
+
diff --git a/zipr_unpin_plugin/unpin_mips32.cpp b/zipr_unpin_plugin/unpin_mips32.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..48c4facd193a832d50a452b8d92fe917cfcb4d7c
--- /dev/null
+++ b/zipr_unpin_plugin/unpin_mips32.cpp
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
+ *
+ * This software is furnished under a license and/or other restrictive
+ * terms and may be used and copied only in accordance with such terms
+ * and the inclusion of the above copyright notice. This software or
+ * any other copies thereof may not be provided or otherwise made
+ * available to any other person without the express written consent
+ * of an authorized representative of Zephyr Software LCC. Title to,
+ * ownership of, and all rights in the software is retained by
+ * Zephyr Software LCC.
+ *
+ * Zephyr Software LLC. Proprietary Information
+ *
+ * Unless otherwise specified, the information contained in this
+ * directory, following this legend, and/or referenced herein is
+ * Zephyr Software LLC. (Zephyr) Proprietary Information.
+ *
+ * CONTACT
+ *
+ * For technical assistance, contact Zephyr Software LCC. at:
+ *
+ *
+ * Zephyr Software, LLC
+ * 2040 Tremont Rd
+ * Charlottesville, VA 22911
+ *
+ * E-mail: jwd@zephyr-software.com
+ **************************************************************************/
+
+
+#include <string>
+#include <algorithm>
+#include "unpin.h"
+#include <memory>
+#include <inttypes.h>
+#include <stdint.h>
+#include <limits.h>
+#include <irdb-util>
+
+
+using namespace IRDB_SDK;
+using namespace std;
+using namespace Zipr_SDK;
+
+static inline uint32_t rotr32 (uint32_t n, unsigned int c)
+{
+  const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);
+
+  // assert ( (c<=mask) &&"rotate by type width or more");
+  c &= mask;
+  return (n>>c) | (n<<( (-c)&mask ));
+}
+
+
+#define ALLOF(a) begin(a),end(a)
+// per machine stuff
+void UnpinMips32_t::HandleRetAddrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+void UnpinMips32_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+void UnpinMips32_t::HandleAbsptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); } 
+
+
+void UnpinMips32_t::HandleImmedptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+void UnpinMips32_t::HandleCallbackReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{ assert(0); }
+
+
+
+
diff --git a/zipr_unpin_plugin/unpin_x86.cpp b/zipr_unpin_plugin/unpin_x86.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a18a979cfc523ce3f7cfd750847cb6654662eeec
--- /dev/null
+++ b/zipr_unpin_plugin/unpin_x86.cpp
@@ -0,0 +1,258 @@
+/***************************************************************************
+ * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
+ *
+ * This software is furnished under a license and/or other restrictive
+ * terms and may be used and copied only in accordance with such terms
+ * and the inclusion of the above copyright notice. This software or
+ * any other copies thereof may not be provided or otherwise made
+ * available to any other person without the express written consent
+ * of an authorized representative of Zephyr Software LCC. Title to,
+ * ownership of, and all rights in the software is retained by
+ * Zephyr Software LCC.
+ *
+ * Zephyr Software LLC. Proprietary Information
+ *
+ * Unless otherwise specified, the information contained in this
+ * directory, following this legend, and/or referenced herein is
+ * Zephyr Software LLC. (Zephyr) Proprietary Information.
+ *
+ * CONTACT
+ *
+ * For technical assistance, contact Zephyr Software LCC. at:
+ *
+ *
+ * Zephyr Software, LLC
+ * 2040 Tremont Rd
+ * Charlottesville, VA 22911
+ *
+ * E-mail: jwd@zephyr-software.com
+ **************************************************************************/
+
+
+#include <string>
+#include <algorithm>
+#include "unpin.h"
+#include <memory>
+#include <inttypes.h>
+
+
+using namespace IRDB_SDK;
+using namespace std;
+using namespace Zipr_SDK;
+
+#define ALLOF(a) begin(a),end(a)
+
+void UnpinX86_t::HandleRetAddrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	// skip if there's no WRT, that means it's unpinned for something besides a fixed call.
+	if(reloc->getWRT()==NULL)
+		return;
+
+	// getWRT returns an BaseObj, but this reloc type expects an instruction
+	// safe cast and check.
+	auto wrt_insn=dynamic_cast<Instruction_t*>(reloc->getWRT());
+	assert(wrt_insn);
+	if(should_cfi_pin(wrt_insn)) 
+		return;
+
+	auto wrt_insn_location =locMap[wrt_insn];
+	auto from_insn_location=locMap[from_insn];
+
+	// 32-bit code and main executables just push a full 32-bit addr.
+	// if(zo->getELFIO()->get_type()==ET_EXEC)
+	if(zo->getFileIR()->getArchitecture()->getFileType()==adftELFEXE)
+	{
+// not handled in push64_relocs which is disabled for shared objects.
+		// expecting a 32-bit push, length=5
+		assert(from_insn->getDataBits()[0]==0x68);
+		assert(from_insn->getDataBits().size()==5);
+		// down and upcast to ensure we fit in 31-bits.
+		assert(wrt_insn_location == (IRDB_SDK::VirtualOffset_t)(int)wrt_insn_location);
+		assert(sizeof(int)==4); // paranoid.
+
+		unsigned char newpush[5];
+		newpush[0]=0x68;
+		const auto newVal=(int)wrt_insn_location;
+		// *(int*)&newpush[1]=(int)wrt_insn_location;
+		memcpy(&newpush[1],&newVal,sizeof(newVal));
+
+		cout<<"Unpin::Updating push32/push64-exe insn:"
+		    <<dec<<from_insn->getBaseID()<<":"<<from_insn->getDisassembly()<<"@"<<hex<<from_insn_location<<" to point at "
+		    <<dec<<wrt_insn ->getBaseID()<<":"<<wrt_insn ->getDisassembly()<<"@"<<hex<<wrt_insn_location <<endl;
+
+		for(auto i=0U;i<from_insn->getDataBits().size();i++)
+		{ 
+			unsigned char newbyte=newpush[i];
+			ms[from_insn_location+i]=newbyte;
+		}
+	}
+	// shared object
+	// gets a call/sub [$rsp], const pair, handled in push64_relocs
+	// else { }
+
+}
+
+
+void UnpinX86_t::HandlePcrelReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	// decode the instruction and find the pcrel operand
+	const auto disasm=DecodedInstruction_t::factory(from_insn);
+	const auto operands=disasm->getOperands();
+	const auto the_arg_it=find_if(ALLOF(operands),[](const shared_ptr<DecodedOperand_t>& op){ return op->isPcrel(); });
+	const auto bo_wrt=reloc->getWRT();
+	const auto scoop_wrt=dynamic_cast<DataScoop_t*>(reloc->getWRT());
+	const auto insn_wrt=dynamic_cast<Instruction_t*>(reloc->getWRT());
+	assert(the_arg_it!=operands.end());
+	const auto the_arg=*the_arg_it;
+
+	// get the new insn addr 	
+	const auto from_insn_location_with_filebase = (VirtualOffset_t)locMap[from_insn];
+	const auto from_insn_location_no_file_base  = from_insn_location_with_filebase - firp.getArchitecture()->getFileBase();
+
+	// get WRT info
+	IRDB_SDK::VirtualOffset_t to_addr=0xdeadbeef; // noteable value that shouldn't be used.
+	string convert_string;
+
+	if(scoop_wrt)
+	{
+		to_addr=scoop_wrt->getStart()->getVirtualOffset();
+		convert_string=string("scoop ")+scoop_wrt->getName();
+	}
+	else if(insn_wrt)
+	{
+
+		to_addr=locMap[insn_wrt];
+		convert_string=string("insn ")+to_string(insn_wrt->getBaseID())+
+			       ":"+insn_wrt->getDisassembly();
+	}
+	else 
+	{
+		assert(bo_wrt==nullptr);
+		to_addr=0; /* no WRT obj */
+		convert_string=string("no-object");
+	}
+
+	const auto rel_addr1=the_arg->getMemoryDisplacement()+from_insn->getDataBits().size();
+	const auto disp_offset=(int)disasm->getMemoryDisplacementOffset(the_arg.get(),from_insn); 
+	const auto disp_size=(int)the_arg->getMemoryDisplacementEncodingSize(); 
+	assert(disp_size==4);
+	assert(0<disp_offset && (int64_t)disp_offset<=(int64_t)from_insn->getDataBits().size() - disp_size);
+		
+	const auto new_disp=(int)(rel_addr1 + to_addr - from_insn->getDataBits().size()-from_insn_location_no_file_base);
+	const auto newbits=from_insn->getDataBits().replace(disp_offset, disp_size, (char*)&new_disp, disp_size); 
+	from_insn->setDataBits(newbits);
+	ms.plopBytes(from_insn_location_with_filebase, newbits.c_str(), newbits.size());
+	const auto disasm2=DecodedInstruction_t::factory(from_insn);
+	cout << "unpin:pcrel:new_disp=" << hex << new_disp << endl;
+	cout << "unpin:pcrel:new_insn_addr=" << hex << from_insn_location_with_filebase << endl;
+	cout << "unpin:pcrel:Converting " << hex << from_insn->getBaseID() << ":" << disasm->getDisassembly() 
+	     << " to " << disasm2->getDisassembly() << " wrt " << convert_string << endl;
+
+}
+
+void UnpinX86_t::HandleAbsptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	// decode the instruction
+	const auto disasm=DecodedInstruction_t::factory(from_insn);
+	const auto operands=disasm->getOperands();
+
+	// find the memory operand
+	// push/pop from memory might have a memory operand with no string to represent the implicit stack operand.
+	const auto the_arg_it=find_if(ALLOF(operands),[](const shared_ptr<DecodedOperand_t>& op){ return op->isMemory() && op->getString()!=""; });
+	const auto wrt=dynamic_cast<DataScoop_t*>(reloc->getWRT());
+
+	// assert we found the right thing
+	assert(wrt);
+	assert(the_arg_it!=operands.end());
+	const auto &the_arg=*the_arg_it;
+
+	// extract the info about where the displacement encoding is
+	const auto disp_offset = uint32_t(disasm->getMemoryDisplacementOffset(the_arg.get(),from_insn)); 
+	const auto disp_size   = uint32_t(the_arg->getMemoryDisplacementEncodingSize()); 
+	assert(disp_size==4);
+	assert(0<disp_offset && (int64_t)disp_offset<=(int64_t)from_insn->getDataBits().size() - disp_size);
+	assert(reloc->getWRT());
+
+	// calculate the new displcement
+	const auto new_disp=uint32_t(the_arg->getMemoryDisplacement() + wrt->getStart()->getVirtualOffset() + reloc->getAddend() - firp.getArchitecture()->getFileBase());
+
+	// update the instruction
+	from_insn->setDataBits(from_insn->getDataBits().replace(disp_offset, disp_size, (char*)&new_disp, disp_size));
+
+	// update the instruction in the memory space.
+	const auto from_insn_location=locMap[from_insn];
+	for(unsigned int i=0;i<from_insn->getDataBits().size();i++)
+	{ 
+		const auto newbyte=from_insn->getDataBits()[i];
+		ms[from_insn_location+i]=newbyte;
+
+		//cout<<"Updating push["<<i<<"] from "<<hex<<oldbyte<<" to "<<newbyte<<endl;
+	}
+
+	// decode again for logging
+	const auto disasm2=DecodedInstruction_t::factory(from_insn);
+
+	// log 
+	cout<<"unpin:absptr_to_scoop:Converting "<<hex<<from_insn->getBaseID()<<":"<<disasm->getDisassembly()
+	    <<" to "<<disasm2->getDisassembly() <<" for scoop: "<<wrt->getName()<<endl;
+}
+
+void UnpinX86_t::HandleImmedptrReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	DataScoop_t* wrt=dynamic_cast<DataScoop_t*>(reloc->getWRT());
+	assert(wrt);
+
+	const auto disasm=DecodedInstruction_t::factory(from_insn);
+	VirtualOffset_t rel_addr2=disasm->getImmediate(); 
+	VirtualOffset_t new_addr = rel_addr2 + wrt->getStart()->getVirtualOffset();
+
+	from_insn->setDataBits(from_insn->getDataBits().replace(from_insn->getDataBits().size()-4, 4, (char*)&new_addr, 4));
+
+	IRDB_SDK::VirtualOffset_t from_insn_location=locMap[from_insn];
+	for(unsigned int i=0;i<from_insn->getDataBits().size();i++)
+	{ 
+		unsigned char newbyte=from_insn->getDataBits()[i];
+		ms[from_insn_location+i]=newbyte;
+
+		//cout<<"Updating push["<<i<<"] from "<<hex<<oldbyte<<" to "<<newbyte<<endl;
+	}
+
+	const auto disasm2=DecodedInstruction_t::factory(from_insn);
+	cout<<"unpin:immedptr_to_scoop:Converting "<<hex<<from_insn->getBaseID()<<":"<<disasm->getDisassembly() 
+	    <<" to "<<disasm2->getDisassembly() <<" for scoop: "<<wrt->getName()<<endl;
+
+}
+
+void UnpinX86_t::HandleCallbackReloc(Instruction_t* from_insn, Relocation_t* reloc)
+{
+	char bytes[]={(char)0x48,
+		      (char)0x8d,
+		      (char)0x64,
+		      (char)0x24,
+		      (char)(64/0x08)}; // lea rsp, [rsp+8]
+
+	if (m_verbose)
+		cout << "The call insn is " 
+		     << from_insn->getDataBits().length() << " bytes long." << endl;
+	
+	auto call_addr = locMap[from_insn];
+
+	if (m_verbose) {
+		cout << "Unpin::callback_to_scoop: call_addr " 
+		     << std::hex << call_addr << endl;
+	}
+
+	/*
+	 * Put down the bogus pop.
+	 */
+	auto at = call_addr + from_insn->getDataBits().length();
+	ms.plopBytes(at, bytes, sizeof(bytes));
+
+	/*
+	 * Turn off the following flags so that this
+	 * is left alone when it is being plopped.
+	 */
+	from_insn->setTarget(NULL);
+	from_insn->setCallback("");
+}
+