/***************************************************************************
 * 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