/*
 * This file is part of ltrace.
 * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
 * Copyright (C) 1998,2004,2008,2009 Juan Cespedes
 * Copyright (C) 2006 Ian Wienand
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "config.h"

#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>

#include "bits.h"
#include "common.h"
#include "proc.h"
#include "output.h"
#include "ptrace.h"
#include "regs.h"
#include "type.h"

#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
#endif

#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
# define PTRACE_POKEUSER PTRACE_POKEUSR
#endif

void
get_arch_dep(struct process *proc)
{
	proc_archdep *a;

	if (!proc->arch_ptr)
		proc->arch_ptr = (void *)malloc(sizeof(proc_archdep));
	a = (proc_archdep *) (proc->arch_ptr);
	a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0);
}

/* Returns 0 if not a syscall,
 *         1 if syscall entry, 2 if syscall exit,
 *         3 if arch-specific syscall entry, 4 if arch-specific syscall exit,
 *         -1 on error.
 */
int
syscall_p(struct process *proc, int status, int *sysnum)
{
	if (WIFSTOPPED(status)
	    && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
		uint32_t pc, ip;
		if (arm_get_register(proc, ARM_REG_PC, &pc) < 0
		    || arm_get_register(proc, ARM_REG_IP, &ip) < 0)
			return -1;

		pc = pc - 4;

		/* fetch the SWI instruction */
		unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid,
				       (void *)pc, 0);

		if (insn == 0xef000000 || insn == 0x0f000000
		    || (insn & 0xffff0000) == 0xdf000000) {
			/* EABI syscall */
			uint32_t r7;
			if (arm_get_register(proc, ARM_REG_R7, &r7) < 0)
				return -1;
			*sysnum = r7;
		} else if ((insn & 0xfff00000) == 0xef900000) {
			/* old ABI syscall */
			*sysnum = insn & 0xfffff;
		} else {
			/* TODO: handle swi<cond> variations */
			/* one possible reason for getting in here is that we
			 * are coming from a signal handler, so the current
			 * PC does not point to the instruction just after the
			 * "swi" one. */
			output_line(proc, "unexpected instruction 0x%x at %p",
				    insn, pc);
			return 0;
		}
		if ((*sysnum & 0xf0000) == 0xf0000) {
			/* arch-specific syscall */
			*sysnum &= ~0xf0000;
			return ip ? 4 : 3;
		}
		/* ARM syscall convention: on syscall entry, ip is zero;
		 * on syscall exit, ip is non-zero */
		return ip ? 2 : 1;
	}
	return 0;
}

static arch_addr_t
arm_branch_dest(const arch_addr_t pc, const uint32_t insn)
{
	/* Bits 0-23 are signed immediate value.  */
	return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8;
}

/* Addresses for calling Thumb functions have the bit 0 set.
   Here are some macros to test, set, or clear bit 0 of addresses.  */
/* XXX double cast */
#define IS_THUMB_ADDR(addr)	((uintptr_t)(addr) & 1)
#define MAKE_THUMB_ADDR(addr)	((arch_addr_t)((uintptr_t)(addr) | 1))
#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1))

enum {
	COND_ALWAYS = 0xe,
	COND_NV = 0xf,
	FLAG_C = 0x20000000,
};

static int
arm_get_next_pcs(struct process *proc,
		 const arch_addr_t pc, arch_addr_t next_pcs[2])
{
	uint32_t this_instr;
	uint32_t status;
	if (proc_read_32(proc, pc, &this_instr) < 0
	    || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
		return -1;

	/* In theory, we sometimes don't even need to add any
	 * breakpoints at all.  If the conditional bits of the
	 * instruction indicate that it should not be taken, then we
	 * can just skip it altogether without bothering.  We could
	 * also emulate the instruction under the breakpoint.
	 *
	 * Here, we make it as simple as possible (though We Accept
	 * Patches).  */
	int nr = 0;

	/* ARM can branch either relatively by using a branch
	 * instruction, or absolutely, by doing arbitrary arithmetic
	 * with PC as the destination.  */
	const unsigned cond = BITS(this_instr, 28, 31);
	const unsigned opcode = BITS(this_instr, 24, 27);

	uint32_t operand1, operand2, result = 0;

	if (cond == COND_NV)
		switch (opcode) {
			arch_addr_t addr;
		case 0xa:
		case 0xb:
			/* Branch with Link and change to Thumb.  */
			/* XXX double cast.  */
			addr = (arch_addr_t)
				((uint32_t)arm_branch_dest(pc, this_instr)
				 | (((this_instr >> 24) & 0x1) << 1));
			next_pcs[nr++] = MAKE_THUMB_ADDR(addr);
			break;
		}
	else
		switch (opcode) {
		case 0x0:
		case 0x1:			/* data processing */
		case 0x2:
		case 0x3:
			if (BITS(this_instr, 12, 15) != ARM_REG_PC)
				break;

			if (BITS(this_instr, 22, 25) == 0
			    && BITS(this_instr, 4, 7) == 9) {	/* multiply */
			invalid:
				fprintf(stderr,
				"Invalid update to pc in instruction.\n");
				break;
			}

			/* BX <reg>, BLX <reg> */
			if (BITS(this_instr, 4, 27) == 0x12fff1
			    || BITS(this_instr, 4, 27) == 0x12fff3) {
				enum arm_register reg = BITS(this_instr, 0, 3);
				/* XXX double cast: no need to go
				 * through tmp.  */
				uint32_t tmp;
				if (arm_get_register_offpc(proc, reg, &tmp) < 0)
					return -1;
				next_pcs[nr++] = (arch_addr_t)tmp;
				return 0;
			}

			/* Multiply into PC.  */
			if (arm_get_register_offpc
			    (proc, BITS(this_instr, 16, 19), &operand1) < 0)
				return -1;

			int c = (status & FLAG_C) ? 1 : 0;
			if (BIT(this_instr, 25)) {
				uint32_t immval = BITS(this_instr, 0, 7);
				uint32_t rotate = 2 * BITS(this_instr, 8, 11);
				operand2 = (((immval >> rotate)
					     | (immval << (32 - rotate)))
					    & 0xffffffff);
			} else {
				/* operand 2 is a shifted register.  */
				if (arm_get_shifted_register
				    (proc, this_instr, c, pc, &operand2) < 0)
					return -1;
			}

			switch (BITS(this_instr, 21, 24)) {
			case 0x0:	/*and */
				result = operand1 & operand2;
				break;

			case 0x1:	/*eor */
				result = operand1 ^ operand2;
				break;

			case 0x2:	/*sub */
				result = operand1 - operand2;
				break;

			case 0x3:	/*rsb */
				result = operand2 - operand1;
				break;

			case 0x4:	/*add */
				result = operand1 + operand2;
				break;

			case 0x5:	/*adc */
				result = operand1 + operand2 + c;
				break;

			case 0x6:	/*sbc */
				result = operand1 - operand2 + c;
				break;

			case 0x7:	/*rsc */
				result = operand2 - operand1 + c;
				break;

			case 0x8:
			case 0x9:
			case 0xa:
			case 0xb:	/* tst, teq, cmp, cmn */
				/* Only take the default branch.  */
				result = 0;
				break;

			case 0xc:	/*orr */
				result = operand1 | operand2;
				break;

			case 0xd:	/*mov */
				/* Always step into a function.  */
				result = operand2;
				break;

			case 0xe:	/*bic */
				result = operand1 & ~operand2;
				break;

			case 0xf:	/*mvn */
				result = ~operand2;
				break;
			}

			/* XXX double cast */
			next_pcs[nr++] = (arch_addr_t)result;
			break;

		case 0x4:
		case 0x5:		/* data transfer */
		case 0x6:
		case 0x7:
			/* Ignore if insn isn't load or Rn not PC.  */
			if (!BIT(this_instr, 20)
			    || BITS(this_instr, 12, 15) != ARM_REG_PC)
				break;

			if (BIT(this_instr, 22))
				goto invalid;

			/* byte write to PC */
			uint32_t base;
			if (arm_get_register_offpc
			    (proc, BITS(this_instr, 16, 19), &base) < 0)
				return -1;

			if (BIT(this_instr, 24)) {
				/* pre-indexed */
				int c = (status & FLAG_C) ? 1 : 0;
				uint32_t offset;
				if (BIT(this_instr, 25)) {
					if (arm_get_shifted_register
					    (proc, this_instr, c,
					     pc, &offset) < 0)
						return -1;
				} else {
					offset = BITS(this_instr, 0, 11);
				}

				if (BIT(this_instr, 23))
					base += offset;
				else
					base -= offset;
			}

			/* XXX two double casts.  */
			uint32_t next;
			if (proc_read_32(proc, (arch_addr_t)base, &next) < 0)
				return -1;
			next_pcs[nr++] = (arch_addr_t)next;
			break;

		case 0x8:
		case 0x9:		/* block transfer */
			if (!BIT(this_instr, 20))
				break;
			/* LDM */
			if (BIT(this_instr, 15)) {
				/* Loading pc.  */
				int offset = 0;
				enum arm_register rn = BITS(this_instr, 16, 19);
				uint32_t rn_val;
				if (arm_get_register(proc, rn, &rn_val) < 0)
					return -1;

				int pre = BIT(this_instr, 24);
				if (BIT(this_instr, 23)) {
					/* Bit U = up.  */
					unsigned reglist
						= BITS(this_instr, 0, 14);
					offset = bitcount(reglist) * 4;
					if (pre)
						offset += 4;
				} else if (pre) {
					offset = -4;
				}

				/* XXX double cast.  */
				arch_addr_t addr
					= (arch_addr_t)(rn_val + offset);
				uint32_t next;
				if (proc_read_32(proc, addr, &next) < 0)
					return -1;
				next_pcs[nr++] = (arch_addr_t)next;
			}
			break;

		case 0xb:		/* branch & link */
		case 0xa:		/* branch */
			next_pcs[nr++] = arm_branch_dest(pc, this_instr);
			break;

		case 0xc:
		case 0xd:
		case 0xe:		/* coproc ops */
		case 0xf:		/* SWI */
			break;
		}

	/* Otherwise take the next instruction.  */
	if (cond != COND_ALWAYS || nr == 0)
		next_pcs[nr++] = pc + 4;
	return 0;
}

/* Return the size in bytes of the complete Thumb instruction whose
 * first halfword is INST1.  */

static int
thumb_insn_size (unsigned short inst1)
{
  if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
	  return 4;
  else
	  return 2;
}

static int
thumb_get_next_pcs(struct process *proc,
		   const arch_addr_t pc, arch_addr_t next_pcs[2])
{
	uint16_t inst1;
	uint32_t status;
	if (proc_read_16(proc, pc, &inst1) < 0
	    || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
		return -1;

	int nr = 0;

	/* We currently ignore Thumb-2 conditional execution support
	 * (the IT instruction).  No branches are allowed in IT block,
	 * and it's not legal to jump in the middle of it, so unless
	 * we need to singlestep through large swaths of code, which
	 * we currently don't, we can ignore them.  */

	if ((inst1 & 0xff00) == 0xbd00)	{ /* pop {rlist, pc} */
		/* Fetch the saved PC from the stack.  It's stored
		 * above all of the other registers.  */
		const unsigned offset = bitcount(BITS(inst1, 0, 7)) * 4;
		uint32_t sp;
		uint32_t next;
		/* XXX two double casts */
		if (arm_get_register(proc, ARM_REG_SP, &sp) < 0
		    || proc_read_32(proc, (arch_addr_t)(sp + offset),
				    &next) < 0)
			return -1;
		next_pcs[nr++] = (arch_addr_t)next;
	} else if ((inst1 & 0xf000) == 0xd000) { /* conditional branch */
		const unsigned long cond = BITS(inst1, 8, 11);
		if (cond != 0x0f) { /* SWI */
			next_pcs[nr++] = pc + (SBITS(inst1, 0, 7) << 1);
			if (cond == COND_ALWAYS)
				return 0;
		}
	} else if ((inst1 & 0xf800) == 0xe000) { /* unconditional branch */
		next_pcs[nr++] = pc + (SBITS(inst1, 0, 10) << 1);
	} else if (thumb_insn_size(inst1) == 4) { /* 32-bit instruction */
		unsigned short inst2;
		if (proc_read_16(proc, pc + 2, &inst2) < 0)
			return -1;

		if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) {
			/* Branches and miscellaneous control instructions.  */

			if ((inst2 & 0x1000) != 0
			    || (inst2 & 0xd001) == 0xc000) {
				/* B, BL, BLX.  */

				const int imm1 = SBITS(inst1, 0, 10);
				const unsigned imm2 = BITS(inst2, 0, 10);
				const unsigned j1 = BIT(inst2, 13);
				const unsigned j2 = BIT(inst2, 11);

				int32_t offset
					= ((imm1 << 12) + (imm2 << 1));
				offset ^= ((!j2) << 22) | ((!j1) << 23);

				/* XXX double cast */
				uint32_t next = (uint32_t)(pc + offset);
				/* For BLX make sure to clear the low bits.  */
				if (BIT(inst2, 12) == 0)
					next = next & 0xfffffffc;
				/* XXX double cast */
				next_pcs[nr++] = (arch_addr_t)next;
				return 0;
			} else if (inst1 == 0xf3de
				   && (inst2 & 0xff00) == 0x3f00) {
				/* SUBS PC, LR, #imm8.  */
				uint32_t next;
				if (arm_get_register(proc, ARM_REG_LR,
						     &next) < 0)
					return -1;
				next -= inst2 & 0x00ff;
				/* XXX double cast */
				next_pcs[nr++] = (arch_addr_t)next;
				return 0;
			} else if ((inst2 & 0xd000) == 0x8000
				   && (inst1 & 0x0380) != 0x0380) {
				/* Conditional branch.  */
				const int sign = SBITS(inst1, 10, 10);
				const unsigned imm1 = BITS(inst1, 0, 5);
				const unsigned imm2 = BITS(inst2, 0, 10);
				const unsigned j1 = BIT(inst2, 13);
				const unsigned j2 = BIT(inst2, 11);

				int32_t offset = (sign << 20)
					+ (j2 << 19) + (j1 << 18);
				offset += (imm1 << 12) + (imm2 << 1);
				next_pcs[nr++] = pc + offset;
				if (BITS(inst1, 6, 9) == COND_ALWAYS)
					return 0;
			}
		} else if ((inst1 & 0xfe50) == 0xe810) {
			int load_pc = 1;
			int offset;
			const enum arm_register rn = BITS(inst1, 0, 3);

			if (BIT(inst1, 7) && !BIT(inst1, 8)) {
				/* LDMIA or POP */
				if (!BIT(inst2, 15))
					load_pc = 0;
				offset = bitcount(inst2) * 4 - 4;
			} else if (!BIT(inst1, 7) && BIT(inst1, 8)) {
				/* LDMDB */
				if (!BIT(inst2, 15))
					load_pc = 0;
				offset = -4;
			} else if (BIT(inst1, 7) && BIT(inst1, 8)) {
				/* RFEIA */
				offset = 0;
			} else if (!BIT(inst1, 7) && !BIT(inst1, 8)) {
				/* RFEDB */
				offset = -8;
			} else {
				load_pc = 0;
			}

			if (load_pc) {
				uint32_t addr;
				if (arm_get_register(proc, rn, &addr) < 0)
					return -1;
				arch_addr_t a = (arch_addr_t)(addr + offset);
				uint32_t next;
				if (proc_read_32(proc, a, &next) < 0)
					return -1;
				/* XXX double cast */
				next_pcs[nr++] = (arch_addr_t)next;
			}
		} else if ((inst1 & 0xffef) == 0xea4f
			   && (inst2 & 0xfff0) == 0x0f00) {
			/* MOV PC or MOVS PC.  */
			const enum arm_register rn = BITS(inst2, 0, 3);
			uint32_t next;
			if (arm_get_register(proc, rn, &next) < 0)
				return -1;
			/* XXX double cast */
			next_pcs[nr++] = (arch_addr_t)next;
		} else if ((inst1 & 0xff70) == 0xf850
			   && (inst2 & 0xf000) == 0xf000) {
			/* LDR PC.  */
			const enum arm_register rn = BITS(inst1, 0, 3);
			uint32_t base;
			if (arm_get_register(proc, rn, &base) < 0)
				return -1;

			int load_pc = 1;
			if (rn == ARM_REG_PC) {
				base = (base + 4) & ~(uint32_t)0x3;
				if (BIT(inst1, 7))
					base += BITS(inst2, 0, 11);
				else
					base -= BITS(inst2, 0, 11);
			} else if (BIT(inst1, 7)) {
				base += BITS(inst2, 0, 11);
			} else if (BIT(inst2, 11)) {
				if (BIT(inst2, 10)) {
					if (BIT(inst2, 9))
						base += BITS(inst2, 0, 7);
					else
						base -= BITS(inst2, 0, 7);
				}
			} else if ((inst2 & 0x0fc0) == 0x0000) {
				const int shift = BITS(inst2, 4, 5);
				const enum arm_register rm = BITS(inst2, 0, 3);
				uint32_t v;
				if (arm_get_register(proc, rm, &v) < 0)
					return -1;
				base += v << shift;
			} else {
				/* Reserved.  */
				load_pc = 0;
			}

			if (load_pc) {
				/* xxx double casts */
				uint32_t next;
				if (proc_read_32(proc,
						 (arch_addr_t)base, &next) < 0)
					return -1;
				next_pcs[nr++] = (arch_addr_t)next;
			}
		} else if ((inst1 & 0xfff0) == 0xe8d0
			   && (inst2 & 0xfff0) == 0xf000) {
			/* TBB.  */
			const enum arm_register tbl_reg = BITS(inst1, 0, 3);
			const enum arm_register off_reg = BITS(inst2, 0, 3);

			uint32_t table;
			if (tbl_reg == ARM_REG_PC)
				/* Regcache copy of PC isn't right yet.  */
				/* XXX double cast */
				table = (uint32_t)pc + 4;
			else if (arm_get_register(proc, tbl_reg, &table) < 0)
				return -1;

			uint32_t offset;
			if (arm_get_register(proc, off_reg, &offset) < 0)
				return -1;

			table += offset;
			uint8_t length;
			/* XXX double cast */
			if (proc_read_8(proc, (arch_addr_t)table, &length) < 0)
				return -1;

			next_pcs[nr++] = pc + 2 * length;

		} else if ((inst1 & 0xfff0) == 0xe8d0
			   && (inst2 & 0xfff0) == 0xf010) {
			/* TBH.  */
			const enum arm_register tbl_reg = BITS(inst1, 0, 3);
			const enum arm_register off_reg = BITS(inst2, 0, 3);

			uint32_t table;
			if (tbl_reg == ARM_REG_PC)
				/* Regcache copy of PC isn't right yet.  */
				/* XXX double cast */
				table = (uint32_t)pc + 4;
			else if (arm_get_register(proc, tbl_reg, &table) < 0)
				return -1;

			uint32_t offset;
			if (arm_get_register(proc, off_reg, &offset) < 0)
				return -1;

			table += 2 * offset;
			uint16_t length;
			/* XXX double cast */
			if (proc_read_16(proc, (arch_addr_t)table, &length) < 0)
				return -1;

			next_pcs[nr++] = pc + 2 * length;
		}
	}


	/* Otherwise take the next instruction.  */
	if (nr == 0)
		next_pcs[nr++] = pc + thumb_insn_size(inst1);
	return 0;
}

enum sw_singlestep_status
arch_sw_singlestep(struct process *proc, struct breakpoint *sbp,
		   int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
		   struct sw_singlestep_data *add_cb_data)
{
	const arch_addr_t pc = get_instruction_pointer(proc);

	uint32_t cpsr;
	if (arm_get_register(proc, ARM_REG_CPSR, &cpsr) < 0)
		return SWS_FAIL;

	const unsigned thumb_p = BIT(cpsr, 5);
	arch_addr_t next_pcs[2] = {};
	if ((thumb_p ? &thumb_get_next_pcs
	     : &arm_get_next_pcs)(proc, pc, next_pcs) < 0)
		return SWS_FAIL;

	int i;
	for (i = 0; i < 2; ++i) {
		/* XXX double cast.  */
		arch_addr_t target
			= (arch_addr_t)(((uintptr_t)next_pcs[i]) | thumb_p);
		if (next_pcs[i] != 0 && add_cb(target, add_cb_data) < 0)
			return SWS_FAIL;
	}

	debug(1, "PTRACE_CONT");
	ptrace(PTRACE_CONT, proc->pid, 0, 0);
	return SWS_OK;
}

size_t
arch_type_sizeof(struct process *proc, struct arg_type_info *info)
{
	if (proc == NULL)
		return (size_t)-2;

	switch (info->type) {
	case ARGTYPE_VOID:
		return 0;

	case ARGTYPE_CHAR:
		return 1;

	case ARGTYPE_SHORT:
	case ARGTYPE_USHORT:
		return 2;

	case ARGTYPE_INT:
	case ARGTYPE_UINT:
	case ARGTYPE_LONG:
	case ARGTYPE_ULONG:
	case ARGTYPE_POINTER:
		return 4;

	case ARGTYPE_FLOAT:
		return 4;
	case ARGTYPE_DOUBLE:
		return 8;

	case ARGTYPE_ARRAY:
	case ARGTYPE_STRUCT:
		/* Use default value.  */
		return (size_t)-2;

	default:
		assert(info->type != info->type);
		abort();
	}
}

size_t
arch_type_alignof(struct process *proc, struct arg_type_info *info)
{
	return arch_type_sizeof(proc, info);
}
