trace.c revision 46a41f7c72828f2f43a640e0ce1fbb41d7a196e9
1#include "config.h"
2
3#include <sys/types.h>
4#include <sys/wait.h>
5#include <signal.h>
6#include <sys/ptrace.h>
7#include <asm/ptrace.h>
8#include <elf.h>
9#include <errno.h>
10#include <string.h>
11
12#include "proc.h"
13#include "common.h"
14#include "ptrace.h"
15#include "breakpoint.h"
16#include "type.h"
17
18#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
19# define PTRACE_PEEKUSER PTRACE_PEEKUSR
20#endif
21
22#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
23# define PTRACE_POKEUSER PTRACE_POKEUSR
24#endif
25
26void
27get_arch_dep(Process *proc) {
28	if (proc->arch_ptr == NULL) {
29		proc->arch_ptr = malloc(sizeof(proc_archdep));
30#ifdef __powerpc64__
31		proc->mask_32bit = (proc->e_machine == EM_PPC);
32#endif
33	}
34
35	proc_archdep *a = (proc_archdep *) (proc->arch_ptr);
36	a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0)
37		&& (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0);
38}
39
40#define SYSCALL_INSN   0x44000002
41
42unsigned int greg = 3;
43unsigned int freg = 1;
44
45/* Returns 1 if syscall, 2 if sysret, 0 otherwise. */
46int
47syscall_p(Process *proc, int status, int *sysnum) {
48	if (WIFSTOPPED(status)
49	    && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
50		long pc = (long)get_instruction_pointer(proc);
51		int insn =
52		    (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(long),
53				0);
54
55		if (insn == SYSCALL_INSN) {
56			*sysnum =
57			    (int)ptrace(PTRACE_PEEKUSER, proc->pid,
58					sizeof(long) * PT_R0, 0);
59			if (proc->callstack_depth > 0 &&
60					proc->callstack[proc->callstack_depth - 1].is_syscall &&
61					proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) {
62				return 2;
63			}
64			return 1;
65		}
66	}
67	return 0;
68}
69
70static long
71gimme_arg_regset(enum tof type, Process *proc, int arg_num,
72		 struct arg_type_info *info,
73		 gregset_t *regs, fpregset_t *fpregs)
74{
75	union { long val; float fval; double dval; } cvt;
76
77	if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) {
78		if (freg <= 13 || (proc->mask_32bit && freg <= 8)) {
79			double val = GET_FPREG(*fpregs, freg);
80
81			if (info->type == ARGTYPE_FLOAT)
82				cvt.fval = val;
83			else
84				cvt.dval = val;
85
86			freg++;
87			greg++;
88
89			return cvt.val;
90		}
91	}
92	else if (greg <= 10)
93		return (*regs)[greg++];
94	else {
95#ifdef __powerpc64__
96		if (proc->mask_32bit)
97			return ptrace (PTRACE_PEEKDATA, proc->pid,
98				       proc->stack_pointer + 8 +
99				       sizeof (int) * (arg_num - 8), 0) >> 32;
100		else
101			return ptrace (PTRACE_PEEKDATA, proc->pid,
102				       proc->stack_pointer + 112 +
103				       sizeof (long) * (arg_num - 8), 0);
104#else
105		return ptrace (PTRACE_PEEKDATA, proc->pid,
106			       proc->stack_pointer + 8 +
107			       sizeof (long) * (arg_num - 8), 0);
108#endif
109	}
110
111	return 0;
112}
113
114static long
115gimme_retval(Process *proc, int arg_num, struct arg_type_info *info,
116	     gregset_t *regs, fpregset_t *fpregs)
117{
118	union { long val; float fval; double dval; } cvt;
119	if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) {
120		double val = GET_FPREG(*fpregs, 1);
121
122		if (info->type == ARGTYPE_FLOAT)
123			cvt.fval = val;
124		else
125			cvt.dval = val;
126
127		return cvt.val;
128	}
129	else
130		return (*regs)[3];
131}
132
133/* Grab functions arguments based on the PPC64 ABI.  */
134long
135gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
136{
137	proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
138	if (arch == NULL || !arch->valid)
139		return -1;
140
141	/* Check if we're entering a new function call to list parameters.  If
142	   so, initialize the register control variables to keep track of where
143	   the parameters were stored.  */
144	if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR)
145	    && arg_num == 0) {
146		/* Initialize the set of registrers for parameter passing.  */
147		greg = 3;
148		freg = 1;
149	}
150
151
152	if (type == LT_TOF_FUNCTIONR) {
153		if (arg_num == -1)
154			return gimme_retval(proc, arg_num, info,
155					    &arch->regs, &arch->fpregs);
156		else
157			return gimme_arg_regset(type, proc, arg_num, info,
158						&arch->regs_copy,
159						&arch->fpregs_copy);
160	}
161	else
162		return gimme_arg_regset(type, proc, arg_num, info,
163					&arch->regs, &arch->fpregs);
164}
165
166void
167save_register_args(enum tof type, Process *proc) {
168	proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
169	if (arch == NULL || !arch->valid)
170		return;
171
172	memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs));
173	memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs));
174}
175
176/* The atomic skip code is mostly taken from GDB.  */
177
178/* Instruction masks used during single-stepping of atomic
179 * sequences.  This was lifted from GDB.  */
180#define LWARX_MASK 0xfc0007fe
181#define LWARX_INSTRUCTION 0x7c000028
182#define LDARX_INSTRUCTION 0x7c0000A8
183#define STWCX_MASK 0xfc0007ff
184#define STWCX_INSTRUCTION 0x7c00012d
185#define STDCX_INSTRUCTION 0x7c0001ad
186#define BC_MASK 0xfc000000
187#define BC_INSN 0x40000000
188#define BRANCH_MASK 0xfc000000
189
190/* In plt.h.  XXX make this official interface.  */
191int read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp);
192
193int
194arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp,
195		       int (*add_cb)(void *addr, void *data),
196		       void *add_cb_data)
197{
198	target_address_t ip = get_instruction_pointer(proc);
199	struct breakpoint *other = address2bpstruct(proc->leader, ip);
200
201	debug(1, "arch_atomic_singlestep pid=%d addr=%p %s(%p)",
202	      proc->pid, ip, breakpoint_name(sbp), sbp->addr);
203
204	/* If the original instruction was lwarx/ldarx, we can't
205	 * single-step over it, instead we have to execute the whole
206	 * atomic block at once.  */
207	union {
208		uint32_t insn;
209		char buf[BREAKPOINT_LENGTH];
210	} u;
211	if (other != NULL) {
212		memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH);
213	} else if (read_target_4(proc, ip, &u.insn) < 0) {
214		fprintf(stderr, "couldn't read instruction at IP %p\n", ip);
215		/* Do the normal singlestep.  */
216		return 1;
217	}
218
219	if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION
220	    && (u.insn & LWARX_MASK) != LDARX_INSTRUCTION)
221		return 1;
222
223	debug(1, "singlestep over atomic block at %p", ip);
224
225	int insn_count;
226	target_address_t addr = ip;
227	for (insn_count = 0; ; ++insn_count) {
228		addr += 4;
229		unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0);
230		if (l == (unsigned long)-1 && errno)
231			return -1;
232		uint32_t insn;
233#ifdef __powerpc64__
234		insn = l >> 32;
235#else
236		insn = l;
237#endif
238
239		/* If a conditional branch is found, put a breakpoint
240		 * in its destination address.  */
241		if ((insn & BRANCH_MASK) == BC_INSN) {
242			int immediate = ((insn & 0xfffc) ^ 0x8000) - 0x8000;
243			int absolute = insn & 2;
244
245			/* XXX drop the following casts.  */
246			target_address_t branch_addr;
247			if (absolute)
248				branch_addr = (void *)(uintptr_t)immediate;
249			else
250				branch_addr = addr + (uintptr_t)immediate;
251
252			debug(1, "pid=%d, branch in atomic block from %p to %p",
253			      proc->pid, addr, branch_addr);
254			if (add_cb(branch_addr, add_cb_data) < 0)
255				return -1;
256		}
257
258		/* Assume that the atomic sequence ends with a
259		 * stwcx/stdcx instruction.  */
260		if ((insn & STWCX_MASK) == STWCX_INSTRUCTION
261		    || (insn & STWCX_MASK) == STDCX_INSTRUCTION) {
262			debug(1, "pid=%d, found end of atomic block %p at %p",
263			      proc->pid, ip, addr);
264			break;
265		}
266
267		/* Arbitrary cut-off.  If we didn't find the
268		 * terminating instruction by now, just give up.  */
269		if (insn_count > 16) {
270			fprintf(stderr, "[%d] couldn't find end of atomic block"
271				" at %p\n", proc->pid, ip);
272			return -1;
273		}
274	}
275
276	/* Put the breakpoint to the next instruction.  */
277	addr += 4;
278	if (add_cb(addr, add_cb_data) < 0)
279		return -1;
280
281	debug(1, "PTRACE_CONT");
282	ptrace(PTRACE_CONT, proc->pid, 0, 0);
283	return 0;
284}
285