trace.c revision e99af270a60891e68d465c4cd97dbe29cd1a05e4
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(Process *proc) {
52	proc_archdep *a;
53
54	if (!proc->arch_ptr)
55		proc->arch_ptr = (void *)malloc(sizeof(proc_archdep));
56	a = (proc_archdep *) (proc->arch_ptr);
57	a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0);
58}
59
60/* Returns 0 if not a syscall,
61 *         1 if syscall entry, 2 if syscall exit,
62 *         3 if arch-specific syscall entry, 4 if arch-specific syscall exit,
63 *         -1 on error.
64 */
65int
66syscall_p(Process *proc, int status, int *sysnum) {
67	if (WIFSTOPPED(status)
68	    && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
69		/* get the user's pc (plus 8) */
70		unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
71		pc = pc - 4;
72		/* fetch the SWI instruction */
73		unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid,
74				       (void *)pc, 0);
75		int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0);
76
77		if (insn == 0xef000000 || insn == 0x0f000000
78		    || (insn & 0xffff0000) == 0xdf000000) {
79			/* EABI syscall */
80			*sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0);
81		} else if ((insn & 0xfff00000) == 0xef900000) {
82			/* old ABI syscall */
83			*sysnum = insn & 0xfffff;
84		} else {
85			/* TODO: handle swi<cond> variations */
86			/* one possible reason for getting in here is that we
87			 * are coming from a signal handler, so the current
88			 * PC does not point to the instruction just after the
89			 * "swi" one. */
90			output_line(proc, "unexpected instruction 0x%x at %p",
91				    insn, pc);
92			return 0;
93		}
94		if ((*sysnum & 0xf0000) == 0xf0000) {
95			/* arch-specific syscall */
96			*sysnum &= ~0xf0000;
97			return ip ? 4 : 3;
98		}
99		/* ARM syscall convention: on syscall entry, ip is zero;
100		 * on syscall exit, ip is non-zero */
101		return ip ? 2 : 1;
102	}
103	return 0;
104}
105
106long
107gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
108{
109	proc_archdep *a = (proc_archdep *) proc->arch_ptr;
110
111	if (arg_num == -1) {	/* return value */
112		return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0);
113	}
114
115	/* deal with the ARM calling conventions */
116	if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
117		if (arg_num < 4) {
118			if (a->valid && type == LT_TOF_FUNCTION)
119				return a->regs.uregs[arg_num];
120			if (a->valid && type == LT_TOF_FUNCTIONR)
121				return a->func_arg[arg_num];
122			return ptrace(PTRACE_PEEKUSER, proc->pid,
123				      (void *)(4 * arg_num), 0);
124		} else {
125			return ptrace(PTRACE_PEEKDATA, proc->pid,
126				      proc->stack_pointer + 4 * (arg_num - 4),
127				      0);
128		}
129	} else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
130		if (arg_num < 5) {
131			if (a->valid && type == LT_TOF_SYSCALL)
132				return a->regs.uregs[arg_num];
133			if (a->valid && type == LT_TOF_SYSCALLR)
134				return a->sysc_arg[arg_num];
135			return ptrace(PTRACE_PEEKUSER, proc->pid,
136				      (void *)(4 * arg_num), 0);
137		} else {
138			return ptrace(PTRACE_PEEKDATA, proc->pid,
139				      proc->stack_pointer + 4 * (arg_num - 5),
140				      0);
141		}
142	} else {
143		fprintf(stderr, "gimme_arg called with wrong arguments\n");
144		exit(1);
145	}
146
147	return 0;
148}
149