summaryrefslogtreecommitdiff
path: root/sysdeps/linux-gnu/arm/regs.c
blob: e9e825ec405f6b6f295533f1e7af5bb73bd768c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * This file is part of ltrace.
 * Copyright (C) 2013 Petr Machata, Red Hat Inc.
 * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
 * Copyright (C) 2009 Juan Cespedes
 *
 * 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 <sys/types.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <errno.h>

#include "proc.h"
#include "common.h"
#include "regs.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

int
arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp)
{
	errno = 0;
	long l = ptrace(PTRACE_PEEKUSER, proc->pid, (void *)(reg * 4L), 0);
	if (l == -1 && errno != 0)
		return -1;
	*lp = (uint32_t)l;
	return 0;
}

int
arm_set_register(struct process *proc, enum arm_register reg, uint32_t lp)
{
	return ptrace(PTRACE_PEEKUSER, proc->pid,
		      (void *)(reg * 4L), (void *)lp);
}

int
arm_get_register_offpc(struct process *proc, enum arm_register reg,
		       uint32_t *lp)
{
	if (arm_get_register(proc, reg, lp) < 0)
		return -1;
	if (reg == ARM_REG_PC)
		*lp += 8;
	return 0;
}

int
arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
			 arch_addr_t pc_val, uint32_t *lp)
{
	enum arm_register rm = BITS(inst, 0, 3);
	unsigned long shifttype = BITS(inst, 5, 6);

	uint32_t shift;
	if (BIT(inst, 4)) {
		if (arm_get_register_offpc(proc, BITS(inst, 8, 11), &shift) < 0)
			return -1;
		shift &= 0xff;
	} else {
		shift = BITS(inst, 7, 11);
	}

	uint32_t res;
	if (rm == ARM_REG_PC)
		/* xxx double cast */
		res = (uintptr_t)pc_val + (BIT(inst, 4) ? 12 : 8);
	else if (arm_get_register(proc, rm, &res) < 0)
		return -1;

	switch (shifttype) {
	case 0:			/* LSL */
		res = shift >= 32 ? 0 : res << shift;
		break;

	case 1:			/* LSR */
		res = shift >= 32 ? 0 : res >> shift;
		break;

	case 2:			/* ASR */
		if (shift >= 32)
			shift = 31;
		res = ((res & 0x80000000L)
		       ? ~((~res) >> shift) : res >> shift);
		break;

	case 3:			/* ROR/RRX */
		shift &= 31;
		if (shift == 0)
			res = (res >> 1) | (carry ? 0x80000000L : 0);
		else
			res = (res >> shift) | (res << (32 - shift));
		break;
	}

	*lp = res & 0xffffffff;
	return 0;
}

static arch_addr_t
get_register_nocheck(struct process *proc, enum arm_register r)
{
	uint32_t reg;
	if (arm_get_register(proc, r, &reg) < 0)
		/* XXX double cast. */
		return (arch_addr_t)-1;
	/* XXX double cast.  */
	return (arch_addr_t)(uintptr_t)reg;
}

arch_addr_t
get_instruction_pointer(struct process *proc)
{
	return get_register_nocheck(proc, ARM_REG_PC);
}

void
set_instruction_pointer(struct process *proc, arch_addr_t addr)
{
	/* XXX double cast.  */
	arm_set_register(proc, ARM_REG_PC, (uint32_t)addr);
}

void *
get_stack_pointer(struct process *proc)
{
	return get_register_nocheck(proc, ARM_REG_SP);
}

arch_addr_t
get_return_addr(struct process *proc, arch_addr_t stack_pointer)
{
	return get_register_nocheck(proc, ARM_REG_LR);
}