Home | History | Annotate | Download | only in arm
      1 /*
      2  * This file is part of ltrace.
      3  * Copyright (C) 2013 Petr Machata, Red Hat Inc.
      4  * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
      5  * Copyright (C) 2009 Juan Cespedes
      6  *
      7  * This program is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU General Public License as
      9  * published by the Free Software Foundation; either version 2 of the
     10  * License, or (at your option) any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but
     13  * WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU General Public License
     18  * along with this program; if not, write to the Free Software
     19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
     20  * 02110-1301 USA
     21  */
     22 
     23 #include "config.h"
     24 
     25 #include <sys/types.h>
     26 #include <sys/ptrace.h>
     27 #include <asm/ptrace.h>
     28 #include <errno.h>
     29 
     30 #include "proc.h"
     31 #include "common.h"
     32 #include "regs.h"
     33 
     34 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
     35 # define PTRACE_PEEKUSER PTRACE_PEEKUSR
     36 #endif
     37 
     38 #if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
     39 # define PTRACE_POKEUSER PTRACE_POKEUSR
     40 #endif
     41 
     42 int
     43 arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp)
     44 {
     45 	errno = 0;
     46 	long l = ptrace(PTRACE_PEEKUSER, proc->pid, (void *)(reg * 4L), 0);
     47 	if (l == -1 && errno != 0)
     48 		return -1;
     49 	*lp = (uint32_t)l;
     50 	return 0;
     51 }
     52 
     53 int
     54 arm_set_register(struct process *proc, enum arm_register reg, uint32_t lp)
     55 {
     56 	return ptrace(PTRACE_PEEKUSER, proc->pid,
     57 		      (void *)(reg * 4L), (void *)lp);
     58 }
     59 
     60 int
     61 arm_get_register_offpc(struct process *proc, enum arm_register reg,
     62 		       uint32_t *lp)
     63 {
     64 	if (arm_get_register(proc, reg, lp) < 0)
     65 		return -1;
     66 	if (reg == ARM_REG_PC)
     67 		*lp += 8;
     68 	return 0;
     69 }
     70 
     71 int
     72 arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
     73 			 arch_addr_t pc_val, uint32_t *lp)
     74 {
     75 	enum arm_register rm = BITS(inst, 0, 3);
     76 	unsigned long shifttype = BITS(inst, 5, 6);
     77 
     78 	uint32_t shift;
     79 	if (BIT(inst, 4)) {
     80 		if (arm_get_register_offpc(proc, BITS(inst, 8, 11), &shift) < 0)
     81 			return -1;
     82 		shift &= 0xff;
     83 	} else {
     84 		shift = BITS(inst, 7, 11);
     85 	}
     86 
     87 	uint32_t res;
     88 	if (rm == ARM_REG_PC)
     89 		/* xxx double cast */
     90 		res = (uintptr_t)pc_val + (BIT(inst, 4) ? 12 : 8);
     91 	else if (arm_get_register(proc, rm, &res) < 0)
     92 		return -1;
     93 
     94 	switch (shifttype) {
     95 	case 0:			/* LSL */
     96 		res = shift >= 32 ? 0 : res << shift;
     97 		break;
     98 
     99 	case 1:			/* LSR */
    100 		res = shift >= 32 ? 0 : res >> shift;
    101 		break;
    102 
    103 	case 2:			/* ASR */
    104 		if (shift >= 32)
    105 			shift = 31;
    106 		res = ((res & 0x80000000L)
    107 		       ? ~((~res) >> shift) : res >> shift);
    108 		break;
    109 
    110 	case 3:			/* ROR/RRX */
    111 		shift &= 31;
    112 		if (shift == 0)
    113 			res = (res >> 1) | (carry ? 0x80000000L : 0);
    114 		else
    115 			res = (res >> shift) | (res << (32 - shift));
    116 		break;
    117 	}
    118 
    119 	*lp = res & 0xffffffff;
    120 	return 0;
    121 }
    122 
    123 static arch_addr_t
    124 get_register_nocheck(struct process *proc, enum arm_register r)
    125 {
    126 	uint32_t reg;
    127 	if (arm_get_register(proc, r, &reg) < 0)
    128 		/* XXX double cast. */
    129 		return (arch_addr_t)-1;
    130 	/* XXX double cast.  */
    131 	return (arch_addr_t)(uintptr_t)reg;
    132 }
    133 
    134 arch_addr_t
    135 get_instruction_pointer(struct process *proc)
    136 {
    137 	return get_register_nocheck(proc, ARM_REG_PC);
    138 }
    139 
    140 void
    141 set_instruction_pointer(struct process *proc, arch_addr_t addr)
    142 {
    143 	/* XXX double cast.  */
    144 	arm_set_register(proc, ARM_REG_PC, (uint32_t)addr);
    145 }
    146 
    147 void *
    148 get_stack_pointer(struct process *proc)
    149 {
    150 	return get_register_nocheck(proc, ARM_REG_SP);
    151 }
    152 
    153 arch_addr_t
    154 get_return_addr(struct process *proc, arch_addr_t stack_pointer)
    155 {
    156 	return get_register_nocheck(proc, ARM_REG_LR);
    157 }
    158