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(""); +} +