1 /*-
2 * Copyright (c) 2004 Olivier Houchard
3 * Copyright (c) 1994-1998 Mark Brinicombe.
4 * Copyright (c) 1994 Brini.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/proc.h>
34 #include <sys/ptrace.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37
38 #include <machine/machdep.h>
39 #include <machine/db_machdep.h>
40
41 static int
42 ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v)
43 {
44
45 if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v))
46 return (ENOMEM);
47 return (0);
48 }
49
50 static int
51 ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v)
52 {
53
54 if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v))
55 return (ENOMEM);
56 return (0);
57 }
58
59 static u_int
60 ptrace_get_usr_reg(void *cookie, int reg)
61 {
62 int ret;
63 struct thread *td = cookie;
64
65 KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)),
66 ("reg is outside range"));
67
68 switch(reg) {
69 case ARM_REG_NUM_PC:
70 ret = td->td_frame->tf_pc;
71 break;
72 case ARM_REG_NUM_LR:
73 ret = td->td_frame->tf_usr_lr;
74 break;
75 case ARM_REG_NUM_SP:
76 ret = td->td_frame->tf_usr_sp;
77 break;
78 default:
79 ret = *((register_t*)&td->td_frame->tf_r0 + reg);
80 break;
81 }
82
83 return (ret);
84 }
85
86 static u_int
87 ptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val)
88 {
89 struct thread *td = cookie;
90 u_int error;
91
92 error = ptrace_read_int(td, offset, val);
93
94 return (error);
95 }
96
97 /**
98 * This function parses current instruction opcode and decodes
99 * any possible jump (change in PC) which might occur after
100 * the instruction is executed.
101 *
102 * @param td Thread structure of analysed task
103 * @param cur_instr Currently executed instruction
104 * @param alt_next_address Pointer to the variable where
105 * the destination address of the
106 * jump instruction shall be stored.
107 *
108 * @return <0> when jump is possible
109 * <EINVAL> otherwise
110 */
111 static int
112 ptrace_get_alternative_next(struct thread *td, uint32_t cur_instr,
113 uint32_t *alt_next_address)
114 {
115 int error;
116
117 if (inst_branch(cur_instr) || inst_call(cur_instr) ||
118 inst_return(cur_instr)) {
119 error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc,
120 alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int);
121
122 return (error);
123 }
124
125 return (EINVAL);
126 }
127
128 int
129 ptrace_single_step(struct thread *td)
130 {
131 struct proc *p;
132 int error, error_alt;
133 uint32_t cur_instr, alt_next = 0;
134
135 /* TODO: This needs to be updated for Thumb-2 */
136 if ((td->td_frame->tf_spsr & PSR_T) != 0)
137 return (EINVAL);
138
139 KASSERT(td->td_md.md_ptrace_instr == 0,
140 ("Didn't clear single step"));
141 KASSERT(td->td_md.md_ptrace_instr_alt == 0,
142 ("Didn't clear alternative single step"));
143 p = td->td_proc;
144 PROC_UNLOCK(p);
145
146 error = ptrace_read_int(td, td->td_frame->tf_pc,
147 &cur_instr);
148 if (error)
149 goto out;
150
151 error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE,
152 &td->td_md.md_ptrace_instr);
153 if (error == 0) {
154 error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE,
155 PTRACE_BREAKPOINT);
156 if (error) {
157 td->td_md.md_ptrace_instr = 0;
158 } else {
159 td->td_md.md_ptrace_addr = td->td_frame->tf_pc +
160 INSN_SIZE;
161 }
162 }
163
164 error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next);
165 if (error_alt == 0) {
166 error_alt = ptrace_read_int(td, alt_next,
167 &td->td_md.md_ptrace_instr_alt);
168 if (error_alt) {
169 td->td_md.md_ptrace_instr_alt = 0;
170 } else {
171 error_alt = ptrace_write_int(td, alt_next,
172 PTRACE_BREAKPOINT);
173 if (error_alt)
174 td->td_md.md_ptrace_instr_alt = 0;
175 else
176 td->td_md.md_ptrace_addr_alt = alt_next;
177 }
178 }
179
180 out:
181 PROC_LOCK(p);
182 return ((error != 0) && (error_alt != 0));
183 }
184
185 int
186 ptrace_clear_single_step(struct thread *td)
187 {
188 struct proc *p;
189
190 /* TODO: This needs to be updated for Thumb-2 */
191 if ((td->td_frame->tf_spsr & PSR_T) != 0)
192 return (EINVAL);
193
194 if (td->td_md.md_ptrace_instr != 0) {
195 p = td->td_proc;
196 PROC_UNLOCK(p);
197 ptrace_write_int(td, td->td_md.md_ptrace_addr,
198 td->td_md.md_ptrace_instr);
199 PROC_LOCK(p);
200 td->td_md.md_ptrace_instr = 0;
201 }
202
203 if (td->td_md.md_ptrace_instr_alt != 0) {
204 p = td->td_proc;
205 PROC_UNLOCK(p);
206 ptrace_write_int(td, td->td_md.md_ptrace_addr_alt,
207 td->td_md.md_ptrace_instr_alt);
208 PROC_LOCK(p);
209 td->td_md.md_ptrace_instr_alt = 0;
210 }
211
212 return (0);
213 }
214
215 int
216 ptrace_set_pc(struct thread *td, unsigned long addr)
217 {
218 td->td_frame->tf_pc = addr;
219 return (0);
220 }
221
222 int
223 arm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc,
224 u_int (*fetch_reg)(void*, int),
225 u_int (*read_int)(void*, vm_offset_t, u_int*))
226 {
227 u_int addr, nregs, offset = 0;
228 int error = 0;
229
230 switch ((insn >> 24) & 0xf) {
231 case 0x2: /* add pc, reg1, #value */
232 case 0x0: /* add pc, reg1, reg2, lsl #offset */
233 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
234 if (((insn >> 16) & 0xf) == 15)
235 addr += 8;
236 if (insn & 0x0200000) {
237 offset = (insn >> 7) & 0x1e;
238 offset = (insn & 0xff) << (32 - offset) |
239 (insn & 0xff) >> offset;
240 } else {
241 offset = fetch_reg(cookie, insn & 0x0f);
242 if ((insn & 0x0000ff0) != 0x00000000) {
243 if (insn & 0x10)
244 nregs = fetch_reg(cookie,
245 (insn >> 8) & 0xf);
246 else
247 nregs = (insn >> 7) & 0x1f;
248 switch ((insn >> 5) & 3) {
249 case 0:
250 /* lsl */
251 offset = offset << nregs;
252 break;
253 case 1:
254 /* lsr */
255 offset = offset >> nregs;
256 break;
257 default:
258 break; /* XXX */
259 }
260 }
261 *new_pc = addr + offset;
262 return (0);
263 }
264
265 case 0xa: /* b ... */
266 case 0xb: /* bl ... */
267 addr = ((insn << 2) & 0x03ffffff);
268 if (addr & 0x02000000)
269 addr |= 0xfc000000;
270 *new_pc = (pc + 8 + addr);
271 return (0);
272 case 0x7: /* ldr pc, [pc, reg, lsl #2] */
273 addr = fetch_reg(cookie, insn & 0xf);
274 addr = pc + 8 + (addr << 2);
275 error = read_int(cookie, addr, &addr);
276 *new_pc = addr;
277 return (error);
278 case 0x1: /* mov pc, reg */
279 *new_pc = fetch_reg(cookie, insn & 0xf);
280 return (0);
281 case 0x4:
282 case 0x5: /* ldr pc, [reg] */
283 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
284 /* ldr pc, [reg, #offset] */
285 if (insn & (1 << 24))
286 offset = insn & 0xfff;
287 if (insn & 0x00800000)
288 addr += offset;
289 else
290 addr -= offset;
291 error = read_int(cookie, addr, &addr);
292 *new_pc = addr;
293
294 return (error);
295 case 0x8: /* ldmxx reg, {..., pc} */
296 case 0x9:
297 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
298 nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
299 nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
300 nregs = (nregs + (nregs >> 4)) & 0x0f0f;
301 nregs = (nregs + (nregs >> 8)) & 0x001f;
302 switch ((insn >> 23) & 0x3) {
303 case 0x0: /* ldmda */
304 addr = addr - 0;
305 break;
306 case 0x1: /* ldmia */
307 addr = addr + 0 + ((nregs - 1) << 2);
308 break;
309 case 0x2: /* ldmdb */
310 addr = addr - 4;
311 break;
312 case 0x3: /* ldmib */
313 addr = addr + 4 + ((nregs - 1) << 2);
314 break;
315 }
316 error = read_int(cookie, addr, &addr);
317 *new_pc = addr;
318
319 return (error);
320 default:
321 return (EINVAL);
322 }
323 }
Cache object: 4751c4cb5948495d43c8e13a7f2fe560
|