trace.c revision 929bd57ca202fd2f2e8485ebf65d683e664f67b5
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2012 Petr Machata, Red Hat Inc.
4 * Copyright (C) 1998,2004,2008,2009 Juan Cespedes
5 * Copyright (C) 2006 Ian Wienand
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 <string.h>
26#include <sys/types.h>
27#include <sys/wait.h>
28#include <signal.h>
29#include <sys/ptrace.h>
30#include <asm/ptrace.h>
31
32#include "proc.h"
33#include "common.h"
34#include "output.h"
35#include "ptrace.h"
36
37#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
38# define PTRACE_PEEKUSER PTRACE_PEEKUSR
39#endif
40
41#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
42# define PTRACE_POKEUSER PTRACE_POKEUSR
43#endif
44
45#define off_r0 ((void *)0)
46#define off_r7 ((void *)28)
47#define off_ip ((void *)48)
48#define off_pc ((void *)60)
49
50void
51get_arch_dep(struct process *proc)
52{
53	proc_archdep *a;
54
55	if (!proc->arch_ptr)
56		proc->arch_ptr = (void *)malloc(sizeof(proc_archdep));
57	a = (proc_archdep *) (proc->arch_ptr);
58	a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0);
59}
60
61/* Returns 0 if not a syscall,
62 *         1 if syscall entry, 2 if syscall exit,
63 *         3 if arch-specific syscall entry, 4 if arch-specific syscall exit,
64 *         -1 on error.
65 */
66int
67syscall_p(struct process *proc, int status, int *sysnum)
68{
69	if (WIFSTOPPED(status)
70	    && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
71		/* get the user's pc (plus 8) */
72		unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
73		pc = pc - 4;
74		/* fetch the SWI instruction */
75		unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid,
76				       (void *)pc, 0);
77		int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0);
78
79		if (insn == 0xef000000 || insn == 0x0f000000
80		    || (insn & 0xffff0000) == 0xdf000000) {
81			/* EABI syscall */
82			*sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0);
83		} else if ((insn & 0xfff00000) == 0xef900000) {
84			/* old ABI syscall */
85			*sysnum = insn & 0xfffff;
86		} else {
87			/* TODO: handle swi<cond> variations */
88			/* one possible reason for getting in here is that we
89			 * are coming from a signal handler, so the current
90			 * PC does not point to the instruction just after the
91			 * "swi" one. */
92			output_line(proc, "unexpected instruction 0x%x at %p",
93				    insn, pc);
94			return 0;
95		}
96		if ((*sysnum & 0xf0000) == 0xf0000) {
97			/* arch-specific syscall */
98			*sysnum &= ~0xf0000;
99			return ip ? 4 : 3;
100		}
101		/* ARM syscall convention: on syscall entry, ip is zero;
102		 * on syscall exit, ip is non-zero */
103		return ip ? 2 : 1;
104	}
105	return 0;
106}
107
108long
109gimme_arg(enum tof type, struct process *proc, int arg_num,
110	  struct arg_type_info *info)
111{
112	proc_archdep *a = (proc_archdep *) proc->arch_ptr;
113
114	if (arg_num == -1) {	/* return value */
115		return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0);
116	}
117
118	/* deal with the ARM calling conventions */
119	if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
120		if (arg_num < 4) {
121			if (a->valid && type == LT_TOF_FUNCTION)
122				return a->regs.uregs[arg_num];
123			if (a->valid && type == LT_TOF_FUNCTIONR)
124				return a->func_arg[arg_num];
125			return ptrace(PTRACE_PEEKUSER, proc->pid,
126				      (void *)(4 * arg_num), 0);
127		} else {
128			return ptrace(PTRACE_PEEKDATA, proc->pid,
129				      proc->stack_pointer + 4 * (arg_num - 4),
130				      0);
131		}
132	} else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
133		if (arg_num < 5) {
134			if (a->valid && type == LT_TOF_SYSCALL)
135				return a->regs.uregs[arg_num];
136			if (a->valid && type == LT_TOF_SYSCALLR)
137				return a->sysc_arg[arg_num];
138			return ptrace(PTRACE_PEEKUSER, proc->pid,
139				      (void *)(4 * arg_num), 0);
140		} else {
141			return ptrace(PTRACE_PEEKDATA, proc->pid,
142				      proc->stack_pointer + 4 * (arg_num - 5),
143				      0);
144		}
145	} else {
146		fprintf(stderr, "gimme_arg called with wrong arguments\n");
147		exit(1);
148	}
149
150	return 0;
151}
152