ltrace/ltrace-0.7.2-arm.patch
2013-01-31 22:38:07 +01:00

1685 lines
49 KiB
Diff

diff --git a/Makefile.am b/Makefile.am
index c3356de..141ff85 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
# This file is part of ltrace.
-# Copyright (C) 2012 Petr Machata, Red Hat Inc.
+# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
# Copyright (C) 2010 Marc Kleine-Budde, Pengutronix
# Copyright (C) 2010 Zachary T Welch, CodeSourcery
#
@@ -33,6 +33,7 @@ noinst_LTLIBRARIES = \
libltrace.la
libltrace_la_SOURCES = \
+ bits.c \
breakpoints.c \
debug.c \
demangle.c \
@@ -83,6 +84,7 @@ ltrace_LDADD = \
noinst_HEADERS = \
+ bits.h \
backend.h \
breakpoint.h \
common.h \
diff --git a/backend.h b/backend.h
index cfac65e..a9de3b4 100644
--- a/backend.h
+++ b/backend.h
@@ -107,10 +107,6 @@ void *get_stack_pointer(struct process *proc);
* function returns. */
void *get_return_addr(struct process *proc, void *stack_pointer);
-/* Adjust PROC so that when the current function returns, it returns
- * to ADDR. */
-void set_return_addr(struct process *proc, void *addr);
-
/* Enable breakpoint SBP in process PROC. */
void enable_breakpoint(struct process *proc, struct breakpoint *sbp);
diff --git a/bits.c b/bits.c
new file mode 100644
index 0000000..bde2e71
--- /dev/null
+++ b/bits.c
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
+ *
+ * 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 "bits.h"
+
+/* This is called rarely, and any overhead will be lost in ptrace
+ * noise, so the algorithm doesn't need to be terribly clever. For
+ * the same reason we don't bother defining the corresponding _32
+ * variant. */
+unsigned
+bitcount(uint64_t u)
+{
+ int c = 0;
+ for (; u > 0; u &= u - 1)
+ c++;
+ return c;
+}
diff --git a/bits.h b/bits.h
new file mode 100644
index 0000000..7dbe478
--- /dev/null
+++ b/bits.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
+ *
+ * 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
+ */
+
+#ifndef _BITS_H_
+#define _BITS_H_
+
+#include <stdint.h>
+
+/* Count bits in U that are 1. */
+unsigned bitcount(uint64_t u);
+
+#endif /* _BITS_H_ */
diff --git a/breakpoint.h b/breakpoint.h
index 18af7a9..963cc66 100644
--- a/breakpoint.h
+++ b/breakpoint.h
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2009 Juan Cespedes
*
* This program is free software; you can redistribute it and/or
@@ -82,11 +82,10 @@ int breakpoint_init(struct breakpoint *bp, struct process *proc,
arch_addr_t addr, struct library_symbol *libsym);
/* Make a clone of breakpoint BP into the area of memory pointed to by
- * RETP. The original breakpoint was assigned to process OLD_PROC,
- * the cloned breakpoint will be attached to process NEW_PROC.
+ * RETP. Symbols of cloned breakpoint are looked up in NEW_PROC.
* Returns 0 on success or a negative value on failure. */
int breakpoint_clone(struct breakpoint *retp, struct process *new_proc,
- struct breakpoint *bp, struct process *old_proc);
+ struct breakpoint *bp);
/* Set callbacks. If CBS is non-NULL, then BP->cbs shall be NULL. */
void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs);
diff --git a/breakpoints.c b/breakpoints.c
index 8db4e26..7b5530a 100644
--- a/breakpoints.c
+++ b/breakpoints.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2006,2007,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2006,2007,2011,2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2009 Juan Cespedes
* Copyright (C) 1998,2001,2002,2003,2007,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
@@ -117,7 +117,7 @@ arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp)
#endif
static void
-breakpoint_init_base(struct breakpoint *bp, struct process *proc,
+breakpoint_init_base(struct breakpoint *bp,
arch_addr_t addr, struct library_symbol *libsym)
{
bp->cbs = NULL;
@@ -135,7 +135,7 @@ int
breakpoint_init(struct breakpoint *bp, struct process *proc,
arch_addr_t addr, struct library_symbol *libsym)
{
- breakpoint_init_base(bp, proc, addr, libsym);
+ breakpoint_init_base(bp, addr, libsym);
return arch_breakpoint_init(proc, bp);
}
@@ -157,7 +157,7 @@ breakpoint_destroy(struct breakpoint *bp)
int
breakpoint_clone(struct breakpoint *retp, struct process *new_proc,
- struct breakpoint *bp, struct process *old_proc)
+ struct breakpoint *bp)
{
struct library_symbol *libsym = NULL;
if (bp->libsym != NULL) {
@@ -165,7 +165,7 @@ breakpoint_clone(struct breakpoint *retp, struct process *new_proc,
assert(rc == 0);
}
- breakpoint_init_base(retp, new_proc, bp->addr, libsym);
+ breakpoint_init_base(retp, bp->addr, libsym);
memcpy(retp->orig_value, bp->orig_value, sizeof(bp->orig_value));
retp->enabled = bp->enabled;
if (arch_breakpoint_clone(retp, bp) < 0)
@@ -211,6 +211,22 @@ insert_breakpoint(struct process *proc, void *addr,
assert(addr != 0);
+ /* We first create the breakpoint to find out what it's real
+ * address is. This makes a difference on ARM.
+ *
+ * XXX The real problem here is that to create a return
+ * breakpoint ltrace calls get_return_addr and then
+ * insert_breakpoint. So get_return_addr needs to encode all
+ * the information necessary for breakpoint_init into the
+ * address itself, so ADDR is potentially mangled. We filter
+ * the noise out by first creating the breakpoint on stack,
+ * and then looking at the address of the created breakpoint.
+ * Replacing get_return_addr with get_return_breakpoint might
+ * be a better solution. */
+ struct breakpoint bp;
+ if (breakpoint_init(&bp, proc, addr, libsym) < 0)
+ return NULL;
+
/* XXX what we need to do instead is have a list of
* breakpoints that are enabled at this address. The
* following works if every breakpoint is the same and there's
@@ -218,20 +234,21 @@ insert_breakpoint(struct process *proc, void *addr,
* will suffice, about the only realistic case where we need
* to have more than one breakpoint per address is return from
* a recursive library call. */
- struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
- if (sbp == NULL) {
+ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, bp.addr);
+ if (sbp != NULL) {
+ breakpoint_destroy(&bp);
+ } else {
+ //fprintf(stderr, "new BP at %p\n", addr);
sbp = malloc(sizeof(*sbp));
- if (sbp == NULL
- || breakpoint_init(sbp, proc, addr, libsym) < 0) {
- free(sbp);
- return NULL;
- }
- if (proc_add_breakpoint(leader, sbp) < 0) {
+ if (sbp == NULL) {
fail:
- breakpoint_destroy(sbp);
free(sbp);
+ breakpoint_destroy(&bp);
return NULL;
}
+ memcpy(sbp, &bp, sizeof(*sbp));
+ if (proc_add_breakpoint(leader, sbp) < 0)
+ goto fail;
}
if (breakpoint_turn_on(sbp, proc) < 0) {
diff --git a/handle_event.c b/handle_event.c
index 9dbb696..1eaea09 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -607,7 +607,6 @@ handle_breakpoint(Event *event)
calc_time_spent(event->proc);
}
}
- event->proc->return_addr = brk_addr;
struct library_symbol *libsym =
event->proc->callstack[i].c_un.libfunc;
@@ -663,8 +662,6 @@ handle_breakpoint(Event *event)
if (event->proc->state != STATE_IGNORED
&& sbp->libsym != NULL) {
event->proc->stack_pointer = get_stack_pointer(event->proc);
- event->proc->return_addr =
- get_return_addr(event->proc, event->proc->stack_pointer);
callstack_push_symfunc(event->proc, sbp->libsym);
output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym);
}
@@ -722,9 +719,11 @@ callstack_push_symfunc(struct process *proc, struct library_symbol *sym)
elem->is_syscall = 0;
elem->c_un.libfunc = sym;
- elem->return_addr = proc->return_addr;
- if (elem->return_addr)
- insert_breakpoint(proc, elem->return_addr, NULL);
+ arch_addr_t return_addr = get_return_addr(proc, proc->stack_pointer);
+ struct breakpoint *rbp = NULL;
+ if (return_addr != 0)
+ rbp = insert_breakpoint(proc, return_addr, NULL);
+ elem->return_addr = rbp != NULL ? rbp->addr : 0;
if (opt_T || options.summary) {
struct timezone tz;
diff --git a/lens_default.c b/lens_default.c
index ed3d0e1..47b8c70 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <string.h>
+#include "bits.h"
#include "proc.h"
#include "lens_default.h"
#include "value.h"
@@ -608,15 +609,6 @@ out_bits(FILE *stream, size_t low, size_t high)
return fprintf(stream, "%zd-%zd", low, high);
}
-static unsigned
-bitcount(unsigned u)
-{
- int c = 0;
- for (; u > 0; u &= u - 1)
- c++;
- return c;
-}
-
static int
bitvect_lens_format_cb(struct lens *lens, FILE *stream,
struct value *value, struct value_dict *arguments)
diff --git a/output.c b/output.c
index fe62bb4..f046df8 100644
--- a/output.c
+++ b/output.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2010 Joe Damato
* Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
* Copyright (C) 2006 Paul Gilliam, IBM Corporation
@@ -119,12 +119,15 @@ begin_of_line(struct process *proc, int is_func, int indent)
}
}
if (opt_i) {
- if (is_func)
+ if (is_func) {
+ struct callstack_element *stel
+ = &proc->callstack[proc->callstack_depth - 1];
current_column += fprintf(options.output, "[%p] ",
- proc->return_addr);
- else
+ stel->return_addr);
+ } else {
current_column += fprintf(options.output, "[%p] ",
proc->instruction_pointer);
+ }
}
if (options.indent > 0 && indent) {
output_indent(proc);
diff --git a/proc.c b/proc.c
index db3f645..7dfde7c 100644
--- a/proc.c
+++ b/proc.c
@@ -314,8 +314,7 @@ clone_single_bp(void *key, void *value, void *u)
struct breakpoint *clone = malloc(sizeof(*clone));
if (clone == NULL
- || breakpoint_clone(clone, data->new_proc,
- bp, data->old_proc) < 0) {
+ || breakpoint_clone(clone, data->new_proc, bp) < 0) {
fail:
free(clone);
data->error = -1;
@@ -1050,6 +1049,7 @@ proc_each_symbol(struct process *proc, struct library_symbol *start_after,
return 0; \
}
+DEF_READER(proc_read_8, 8)
DEF_READER(proc_read_16, 16)
DEF_READER(proc_read_32, 32)
DEF_READER(proc_read_64, 64)
diff --git a/proc.h b/proc.h
index 04c0ef7..03708dc 100644
--- a/proc.h
+++ b/proc.h
@@ -65,7 +65,7 @@ struct callstack_element {
struct library_symbol * libfunc;
} c_un;
int is_syscall;
- void * return_addr;
+ arch_addr_t return_addr;
struct timeval time_spent;
struct fetch_context *fetch_context;
struct value_dict *arguments;
@@ -106,7 +106,6 @@ struct process {
/* Arch-dependent: */
void * instruction_pointer;
void * stack_pointer; /* To get return addr, args... */
- void * return_addr;
void * arch_ptr;
/* XXX We would like to replace this with a pointer to ABI
@@ -116,11 +115,6 @@ struct process {
short e_machine;
char e_class;
- /* XXX this shoudl go to ARM's arch_process_data. */
-#ifdef __arm__
- int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */
-#endif
-
#if defined(HAVE_LIBUNWIND)
/* libunwind address space */
unw_addr_space_t unwind_as;
@@ -254,10 +248,11 @@ struct library_symbol *proc_each_symbol
enum callback_status (*cb)(struct library_symbol *, void *),
void *data);
-/* Read 16, 32 or 64-bit quantity located at ADDR in PROC. The
+/* Read 8, 16, 32 or 64-bit quantity located at ADDR in PROC. The
* resulting value is stored in *LP. 0 is returned on success or a
* negative value on failure. This uses umovebytes under the hood
* (see backend.h). */
+int proc_read_8(struct process *proc, arch_addr_t addr, uint8_t *lp);
int proc_read_16(struct process *proc, arch_addr_t addr, uint16_t *lp);
int proc_read_32(struct process *proc, arch_addr_t addr, uint32_t *lp);
int proc_read_64(struct process *proc, arch_addr_t addr, uint64_t *lp);
diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c
index c197225..9ccd8f2 100644
--- a/sysdeps/linux-gnu/alpha/regs.c
+++ b/sysdeps/linux-gnu/alpha/regs.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2004,2008,2009 Juan Cespedes
*
* This program is free software; you can redistribute it and/or
@@ -58,9 +59,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
{
return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 26 /* RA */ , 0);
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr);
-}
diff --git a/sysdeps/linux-gnu/arm/Makefile.am b/sysdeps/linux-gnu/arm/Makefile.am
index 385424c..4b858ec 100644
--- a/sysdeps/linux-gnu/arm/Makefile.am
+++ b/sysdeps/linux-gnu/arm/Makefile.am
@@ -1,4 +1,5 @@
# This file is part of ltrace.
+# Copyright (C) 2013 Petr Machata, Red Hat Inc.
# Copyright (C) 2010 Marc Kleine-Budde, Pengutronix
#
# This program is free software; you can redistribute it and/or
@@ -16,21 +17,11 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
-noinst_LTLIBRARIES = \
- ../libcpu.la
+noinst_LTLIBRARIES = ../libcpu.la
-___libcpu_la_SOURCES = \
- breakpoint.c \
- plt.c \
- regs.c \
- trace.c
+___libcpu_la_SOURCES = breakpoint.c plt.c regs.c trace.c
-noinst_HEADERS = \
- arch.h \
- arch_syscallent.h \
- ptrace.h \
- signalent.h \
- syscallent.h
+noinst_HEADERS = arch.h arch_syscallent.h ptrace.h regs.h signalent.h \
+ syscallent.h
-MAINTAINERCLEANFILES = \
- Makefile.in
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h
index 291443a..71cfd5e 100644
--- a/sysdeps/linux-gnu/arm/arch.h
+++ b/sysdeps/linux-gnu/arm/arch.h
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 1998,2004,2008 Juan Cespedes
*
* This program is free software; you can redistribute it and/or
@@ -31,6 +32,7 @@
#define LT_ELFCLASS ELFCLASS32
#define LT_ELF_MACHINE EM_ARM
+#define ARCH_HAVE_SW_SINGLESTEP
#define ARCH_HAVE_BREAKPOINT_DATA
struct arch_breakpoint_data {
int thumb_mode;
diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c
index 2fb9578..fcd43a7 100644
--- a/sysdeps/linux-gnu/arm/breakpoint.c
+++ b/sysdeps/linux-gnu/arm/breakpoint.c
@@ -94,14 +94,11 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp)
int
arch_breakpoint_init(struct process *proc, struct breakpoint *sbp)
{
- /* XXX That uintptr_t cast is there temporarily until
- * arch_addr_t becomes integral type. */
- int thumb_mode = ((uintptr_t)sbp->addr) & 1;
- if (thumb_mode)
- sbp->addr = (void *)((uintptr_t)sbp->addr & ~1);
- sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode;
- /* XXX This doesn't seem like it belongs here. */
- proc->thumb_mode = 0;
+ /* XXX double cast */
+ sbp->arch.thumb_mode = ((uintptr_t)sbp->addr) & 1;
+ if (sbp->arch.thumb_mode)
+ /* XXX double cast */
+ sbp->addr = (arch_addr_t)((uintptr_t)sbp->addr & ~1);
return 0;
}
diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c
index 377df62..bd86370 100644
--- a/sysdeps/linux-gnu/arm/regs.c
+++ b/sysdeps/linux-gnu/arm/regs.c
@@ -1,5 +1,6 @@
/*
* 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
*
@@ -24,9 +25,11 @@
#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
@@ -37,13 +40,91 @@
#endif
#define off_pc ((void *)60)
-#define off_lr ((void *)56)
#define off_sp ((void *)52)
-void *
+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_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;
+}
+
+arch_addr_t
get_instruction_pointer(struct process *proc)
{
- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
+ uint32_t reg;
+ if (arm_get_register(proc, ARM_REG_PC, &reg) < 0)
+ /* XXX double cast. */
+ return (arch_addr_t)-1;
+ /* XXX double cast. */
+ return (arch_addr_t)(uintptr_t)reg;
}
void
@@ -58,28 +139,13 @@ get_stack_pointer(struct process *proc)
return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0);
}
-/* really, this is given the *stack_pointer expecting
- * a CISC architecture; in our case, we don't need that */
-void *
-get_return_addr(struct process *proc, void *stack_pointer)
-{
- long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0);
-
- /* Remember & unset the thumb mode bit. XXX This is really a
- * bit of a hack, as we assume that the following
- * insert_breakpoint call will be related to this address.
- * This interface should really be get_return_breakpoint, or
- * maybe install_return_breakpoint. */
- proc->thumb_mode = addr & 1;
- if (proc->thumb_mode)
- addr &= ~1;
-
- return (void *)addr;
-}
-
-void
-set_return_addr(struct process *proc, void *addr)
+arch_addr_t
+get_return_addr(struct process *proc, arch_addr_t stack_pointer)
{
- long iaddr = (int)addr | proc->thumb_mode;
- ptrace(PTRACE_POKEUSER, proc->pid, off_lr, (void *)iaddr);
+ uint32_t reg;
+ if (arm_get_register(proc, ARM_REG_LR, &reg) < 0)
+ /* XXX double cast. */
+ return (arch_addr_t)-1;
+ /* XXX double cast. */
+ return (arch_addr_t)(uintptr_t)reg;
}
diff --git a/sysdeps/linux-gnu/arm/regs.h b/sysdeps/linux-gnu/arm/regs.h
new file mode 100644
index 0000000..1566f92
--- /dev/null
+++ b/sysdeps/linux-gnu/arm/regs.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
+ *
+ * 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
+ */
+
+#define SUBMASK(x) ((1L << ((x) + 1)) - 1)
+#define BIT(obj,st) (((obj) >> (st)) & 1)
+#define BITS(obj,st,fn) (((obj) >> (st)) & SUBMASK((fn) - (st)))
+#define SBITS(obj,st,fn) \
+ ((long) (BITS(obj,st,fn) | ((long) BIT(obj,fn) * ~ SUBMASK(fn - st))))
+
+enum arm_register {
+ ARM_REG_SP = 13,
+ ARM_REG_LR = 14,
+ ARM_REG_PC = 15,
+ ARM_REG_CPSR = 16,
+};
+
+/* Write value of register REG to *LP. Return 0 on success or a
+ * negative value on failure. */
+int arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp);
+
+/* Same as above, but if REG==ARM_REG_PC, it returns the value +8. */
+int arm_get_register_offpc(struct process *proc, enum arm_register reg,
+ uint32_t *lp);
+
+/* Same as arm_get_register, but shift is performed depending on
+ * instruction INST. */
+int arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
+ arch_addr_t pc, uint32_t *lp);
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
index fbbf676..8b0734e 100644
--- a/sysdeps/linux-gnu/arm/trace.c
+++ b/sysdeps/linux-gnu/arm/trace.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 1998,2004,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
@@ -29,10 +29,12 @@
#include <sys/ptrace.h>
#include <asm/ptrace.h>
-#include "proc.h"
+#include "bits.h"
#include "common.h"
+#include "proc.h"
#include "output.h"
#include "ptrace.h"
+#include "regs.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -46,6 +48,7 @@
#define off_r7 ((void *)28)
#define off_ip ((void *)48)
#define off_pc ((void *)60)
+#define off_cpsr ((void *)64)
void
get_arch_dep(struct process *proc)
@@ -149,3 +152,560 @@ gimme_arg(enum tof type, struct process *proc, int arg_num,
return 0;
}
+
+static arch_addr_t
+arm_branch_dest(const arch_addr_t pc, const uint32_t insn)
+{
+ /* Bits 0-23 are signed immediate value. */
+ return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8;
+}
+
+/* Addresses for calling Thumb functions have the bit 0 set.
+ Here are some macros to test, set, or clear bit 0 of addresses. */
+/* XXX double cast */
+#define IS_THUMB_ADDR(addr) ((uintptr_t)(addr) & 1)
+#define MAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) | 1))
+#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1))
+
+enum {
+ COND_ALWAYS = 0xe,
+ COND_NV = 0xf,
+ FLAG_C = 0x20000000,
+};
+
+static int
+arm_get_next_pcs(struct process *proc,
+ const arch_addr_t pc, arch_addr_t next_pcs[2])
+{
+ uint32_t this_instr;
+ uint32_t status;
+ if (proc_read_32(proc, pc, &this_instr) < 0
+ || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
+ return -1;
+
+ /* In theory, we sometimes don't even need to add any
+ * breakpoints at all. If the conditional bits of the
+ * instruction indicate that it should not be taken, then we
+ * can just skip it altogether without bothering. We could
+ * also emulate the instruction under the breakpoint.
+ *
+ * Here, we make it as simple as possible (though We Accept
+ * Patches). */
+ int nr = 0;
+
+ /* ARM can branch either relatively by using a branch
+ * instruction, or absolutely, by doing arbitrary arithmetic
+ * with PC as the destination. */
+ const unsigned cond = BITS(this_instr, 28, 31);
+ const unsigned opcode = BITS(this_instr, 24, 27);
+
+ if (cond == COND_NV)
+ switch (opcode) {
+ arch_addr_t addr;
+ case 0xa:
+ case 0xb:
+ /* Branch with Link and change to Thumb. */
+ /* XXX double cast. */
+ addr = (arch_addr_t)
+ ((uint32_t)arm_branch_dest(pc, this_instr)
+ | (((this_instr >> 24) & 0x1) << 1));
+ next_pcs[nr++] = MAKE_THUMB_ADDR(addr);
+ break;
+ }
+ else
+ switch (opcode) {
+ uint32_t operand1, operand2, result = 0;
+ case 0x0:
+ case 0x1: /* data processing */
+ case 0x2:
+ case 0x3:
+ if (BITS(this_instr, 12, 15) != ARM_REG_PC)
+ break;
+
+ if (BITS(this_instr, 22, 25) == 0
+ && BITS(this_instr, 4, 7) == 9) { /* multiply */
+ invalid:
+ fprintf(stderr,
+ "Invalid update to pc in instruction.\n");
+ break;
+ }
+
+ /* BX <reg>, BLX <reg> */
+ if (BITS(this_instr, 4, 27) == 0x12fff1
+ || BITS(this_instr, 4, 27) == 0x12fff3) {
+ enum arm_register reg = BITS(this_instr, 0, 3);
+ /* XXX double cast: no need to go
+ * through tmp. */
+ uint32_t tmp;
+ if (arm_get_register_offpc(proc, reg, &tmp) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)tmp;
+ return 0;
+ }
+
+ /* Multiply into PC. */
+ if (arm_get_register_offpc
+ (proc, BITS(this_instr, 16, 19), &operand1) < 0)
+ return -1;
+
+ int c = (status & FLAG_C) ? 1 : 0;
+ if (BIT(this_instr, 25)) {
+ uint32_t immval = BITS(this_instr, 0, 7);
+ uint32_t rotate = 2 * BITS(this_instr, 8, 11);
+ operand2 = (((immval >> rotate)
+ | (immval << (32 - rotate)))
+ & 0xffffffff);
+ } else {
+ /* operand 2 is a shifted register. */
+ if (arm_get_shifted_register
+ (proc, this_instr, c, pc, &operand2) < 0)
+ return -1;
+ }
+
+ switch (BITS(this_instr, 21, 24)) {
+ case 0x0: /*and */
+ result = operand1 & operand2;
+ break;
+
+ case 0x1: /*eor */
+ result = operand1 ^ operand2;
+ break;
+
+ case 0x2: /*sub */
+ result = operand1 - operand2;
+ break;
+
+ case 0x3: /*rsb */
+ result = operand2 - operand1;
+ break;
+
+ case 0x4: /*add */
+ result = operand1 + operand2;
+ break;
+
+ case 0x5: /*adc */
+ result = operand1 + operand2 + c;
+ break;
+
+ case 0x6: /*sbc */
+ result = operand1 - operand2 + c;
+ break;
+
+ case 0x7: /*rsc */
+ result = operand2 - operand1 + c;
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xa:
+ case 0xb: /* tst, teq, cmp, cmn */
+ /* Only take the default branch. */
+ result = 0;
+ break;
+
+ case 0xc: /*orr */
+ result = operand1 | operand2;
+ break;
+
+ case 0xd: /*mov */
+ /* Always step into a function. */
+ result = operand2;
+ break;
+
+ case 0xe: /*bic */
+ result = operand1 & ~operand2;
+ break;
+
+ case 0xf: /*mvn */
+ result = ~operand2;
+ break;
+ }
+
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)result;
+ break;
+
+ case 0x4:
+ case 0x5: /* data transfer */
+ case 0x6:
+ case 0x7:
+ /* Ignore if insn isn't load or Rn not PC. */
+ if (!BIT(this_instr, 20)
+ || BITS(this_instr, 12, 15) != ARM_REG_PC)
+ break;
+
+ if (BIT(this_instr, 22))
+ goto invalid;
+
+ /* byte write to PC */
+ uint32_t base;
+ if (arm_get_register_offpc
+ (proc, BITS(this_instr, 16, 19), &base) < 0)
+ return -1;
+
+ if (BIT(this_instr, 24)) {
+ /* pre-indexed */
+ int c = (status & FLAG_C) ? 1 : 0;
+ uint32_t offset;
+ if (BIT(this_instr, 25)) {
+ if (arm_get_shifted_register
+ (proc, this_instr, c,
+ pc, &offset) < 0)
+ return -1;
+ } else {
+ offset = BITS(this_instr, 0, 11);
+ }
+
+ if (BIT(this_instr, 23))
+ base += offset;
+ else
+ base -= offset;
+ }
+
+ /* XXX two double casts. */
+ uint32_t next;
+ if (proc_read_32(proc, (arch_addr_t)base, &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ break;
+
+ case 0x8:
+ case 0x9: /* block transfer */
+ if (!BIT(this_instr, 20))
+ break;
+ /* LDM */
+ if (BIT(this_instr, 15)) {
+ /* Loading pc. */
+ int offset = 0;
+ enum arm_register rn = BITS(this_instr, 16, 19);
+ uint32_t rn_val;
+ if (arm_get_register(proc, rn, &rn_val) < 0)
+ return -1;
+
+ int pre = BIT(this_instr, 24);
+ if (BIT(this_instr, 23)) {
+ /* Bit U = up. */
+ unsigned reglist
+ = BITS(this_instr, 0, 14);
+ offset = bitcount(reglist) * 4;
+ if (pre)
+ offset += 4;
+ } else if (pre) {
+ offset = -4;
+ }
+
+ /* XXX double cast. */
+ arch_addr_t addr
+ = (arch_addr_t)(rn_val + offset);
+ uint32_t next;
+ if (proc_read_32(proc, addr, &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ }
+ break;
+
+ case 0xb: /* branch & link */
+ case 0xa: /* branch */
+ next_pcs[nr++] = arm_branch_dest(pc, this_instr);
+ break;
+
+ case 0xc:
+ case 0xd:
+ case 0xe: /* coproc ops */
+ case 0xf: /* SWI */
+ break;
+ }
+
+ /* Otherwise take the next instruction. */
+ if (cond != COND_ALWAYS || nr == 0)
+ next_pcs[nr++] = pc + 4;
+ return 0;
+}
+
+/* Return the size in bytes of the complete Thumb instruction whose
+ * first halfword is INST1. */
+
+static int
+thumb_insn_size (unsigned short inst1)
+{
+ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+ return 4;
+ else
+ return 2;
+}
+
+static int
+thumb_get_next_pcs(struct process *proc,
+ const arch_addr_t pc, arch_addr_t next_pcs[2])
+{
+ uint16_t inst1;
+ uint32_t status;
+ if (proc_read_16(proc, pc, &inst1) < 0
+ || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
+ return -1;
+
+ int nr = 0;
+
+ /* We currently ignore Thumb-2 conditional execution support
+ * (the IT instruction). No branches are allowed in IT block,
+ * and it's not legal to jump in the middle of it, so unless
+ * we need to singlestep through large swaths of code, which
+ * we currently don't, we can ignore them. */
+
+ if ((inst1 & 0xff00) == 0xbd00) { /* pop {rlist, pc} */
+ /* Fetch the saved PC from the stack. It's stored
+ * above all of the other registers. */
+ const unsigned offset = bitcount(BITS(inst1, 0, 7)) * 4;
+ uint32_t sp;
+ uint32_t next;
+ /* XXX two double casts */
+ if (arm_get_register(proc, ARM_REG_SP, &sp) < 0
+ || proc_read_32(proc, (arch_addr_t)(sp + offset),
+ &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ } else if ((inst1 & 0xf000) == 0xd000) { /* conditional branch */
+ const unsigned long cond = BITS(inst1, 8, 11);
+ if (cond != 0x0f) { /* SWI */
+ next_pcs[nr++] = pc + (SBITS(inst1, 0, 7) << 1);
+ if (cond == COND_ALWAYS)
+ return 0;
+ }
+ } else if ((inst1 & 0xf800) == 0xe000) { /* unconditional branch */
+ next_pcs[nr++] = pc + (SBITS(inst1, 0, 10) << 1);
+ } else if (thumb_insn_size(inst1) == 4) { /* 32-bit instruction */
+ unsigned short inst2;
+ if (proc_read_16(proc, pc + 2, &inst2) < 0)
+ return -1;
+
+ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) {
+ /* Branches and miscellaneous control instructions. */
+
+ if ((inst2 & 0x1000) != 0
+ || (inst2 & 0xd001) == 0xc000) {
+ /* B, BL, BLX. */
+
+ const int imm1 = SBITS(inst1, 0, 10);
+ const unsigned imm2 = BITS(inst2, 0, 10);
+ const unsigned j1 = BIT(inst2, 13);
+ const unsigned j2 = BIT(inst2, 11);
+
+ int32_t offset
+ = ((imm1 << 12) + (imm2 << 1));
+ offset ^= ((!j2) << 22) | ((!j1) << 23);
+
+ /* XXX double cast */
+ uint32_t next = (uint32_t)(pc + offset);
+ /* For BLX make sure to clear the low bits. */
+ if (BIT(inst2, 12) == 0)
+ next = next & 0xfffffffc;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ return 0;
+ } else if (inst1 == 0xf3de
+ && (inst2 & 0xff00) == 0x3f00) {
+ /* SUBS PC, LR, #imm8. */
+ uint32_t next;
+ if (arm_get_register(proc, ARM_REG_LR,
+ &next) < 0)
+ return -1;
+ next -= inst2 & 0x00ff;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ return 0;
+ } else if ((inst2 & 0xd000) == 0x8000
+ && (inst1 & 0x0380) != 0x0380) {
+ /* Conditional branch. */
+ const int sign = SBITS(inst1, 10, 10);
+ const unsigned imm1 = BITS(inst1, 0, 5);
+ const unsigned imm2 = BITS(inst2, 0, 10);
+ const unsigned j1 = BIT(inst2, 13);
+ const unsigned j2 = BIT(inst2, 11);
+
+ int32_t offset = (sign << 20)
+ + (j2 << 19) + (j1 << 18);
+ offset += (imm1 << 12) + (imm2 << 1);
+ next_pcs[nr++] = pc + offset;
+ if (BITS(inst1, 6, 9) == COND_ALWAYS)
+ return 0;
+ }
+ } else if ((inst1 & 0xfe50) == 0xe810) {
+ int load_pc = 1;
+ int offset;
+ const enum arm_register rn = BITS(inst1, 0, 3);
+
+ if (BIT(inst1, 7) && !BIT(inst1, 8)) {
+ /* LDMIA or POP */
+ if (!BIT(inst2, 15))
+ load_pc = 0;
+ offset = bitcount(inst2) * 4 - 4;
+ } else if (!BIT(inst1, 7) && BIT(inst1, 8)) {
+ /* LDMDB */
+ if (!BIT(inst2, 15))
+ load_pc = 0;
+ offset = -4;
+ } else if (BIT(inst1, 7) && BIT(inst1, 8)) {
+ /* RFEIA */
+ offset = 0;
+ } else if (!BIT(inst1, 7) && !BIT(inst1, 8)) {
+ /* RFEDB */
+ offset = -8;
+ } else {
+ load_pc = 0;
+ }
+
+ if (load_pc) {
+ uint32_t addr;
+ if (arm_get_register(proc, rn, &addr) < 0)
+ return -1;
+ arch_addr_t a = (arch_addr_t)(addr + offset);
+ uint32_t next;
+ if (proc_read_32(proc, a, &next) < 0)
+ return -1;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ }
+ } else if ((inst1 & 0xffef) == 0xea4f
+ && (inst2 & 0xfff0) == 0x0f00) {
+ /* MOV PC or MOVS PC. */
+ const enum arm_register rn = BITS(inst2, 0, 3);
+ uint32_t next;
+ if (arm_get_register(proc, rn, &next) < 0)
+ return -1;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ } else if ((inst1 & 0xff70) == 0xf850
+ && (inst2 & 0xf000) == 0xf000) {
+ /* LDR PC. */
+ const enum arm_register rn = BITS(inst1, 0, 3);
+ uint32_t base;
+ if (arm_get_register(proc, rn, &base) < 0)
+ return -1;
+
+ int load_pc = 1;
+ if (rn == ARM_REG_PC) {
+ base = (base + 4) & ~(uint32_t)0x3;
+ if (BIT(inst1, 7))
+ base += BITS(inst2, 0, 11);
+ else
+ base -= BITS(inst2, 0, 11);
+ } else if (BIT(inst1, 7)) {
+ base += BITS(inst2, 0, 11);
+ } else if (BIT(inst2, 11)) {
+ if (BIT(inst2, 10)) {
+ if (BIT(inst2, 9))
+ base += BITS(inst2, 0, 7);
+ else
+ base -= BITS(inst2, 0, 7);
+ }
+ } else if ((inst2 & 0x0fc0) == 0x0000) {
+ const int shift = BITS(inst2, 4, 5);
+ const enum arm_register rm = BITS(inst2, 0, 3);
+ uint32_t v;
+ if (arm_get_register(proc, rm, &v) < 0)
+ return -1;
+ base += v << shift;
+ } else {
+ /* Reserved. */
+ load_pc = 0;
+ }
+
+ if (load_pc) {
+ /* xxx double casts */
+ uint32_t next;
+ if (proc_read_32(proc,
+ (arch_addr_t)base, &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ }
+ } else if ((inst1 & 0xfff0) == 0xe8d0
+ && (inst2 & 0xfff0) == 0xf000) {
+ /* TBB. */
+ const enum arm_register tbl_reg = BITS(inst1, 0, 3);
+ const enum arm_register off_reg = BITS(inst2, 0, 3);
+
+ uint32_t table;
+ if (tbl_reg == ARM_REG_PC)
+ /* Regcache copy of PC isn't right yet. */
+ /* XXX double cast */
+ table = (uint32_t)pc + 4;
+ else if (arm_get_register(proc, tbl_reg, &table) < 0)
+ return -1;
+
+ uint32_t offset;
+ if (arm_get_register(proc, off_reg, &offset) < 0)
+ return -1;
+
+ table += offset;
+ uint8_t length;
+ /* XXX double cast */
+ if (proc_read_8(proc, (arch_addr_t)table, &length) < 0)
+ return -1;
+
+ next_pcs[nr++] = pc + 2 * length;
+
+ } else if ((inst1 & 0xfff0) == 0xe8d0
+ && (inst2 & 0xfff0) == 0xf010) {
+ /* TBH. */
+ const enum arm_register tbl_reg = BITS(inst1, 0, 3);
+ const enum arm_register off_reg = BITS(inst2, 0, 3);
+
+ uint32_t table;
+ if (tbl_reg == ARM_REG_PC)
+ /* Regcache copy of PC isn't right yet. */
+ /* XXX double cast */
+ table = (uint32_t)pc + 4;
+ else if (arm_get_register(proc, tbl_reg, &table) < 0)
+ return -1;
+
+ uint32_t offset;
+ if (arm_get_register(proc, off_reg, &offset) < 0)
+ return -1;
+
+ table += 2 * offset;
+ uint16_t length;
+ /* XXX double cast */
+ if (proc_read_16(proc, (arch_addr_t)table, &length) < 0)
+ return -1;
+
+ next_pcs[nr++] = pc + 2 * length;
+ }
+ }
+
+
+ /* Otherwise take the next instruction. */
+ if (nr == 0)
+ next_pcs[nr++] = pc + thumb_insn_size(inst1);
+ return 0;
+}
+
+enum sw_singlestep_status
+arch_sw_singlestep(struct process *proc, struct breakpoint *sbp,
+ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
+ struct sw_singlestep_data *add_cb_data)
+{
+ const arch_addr_t pc = get_instruction_pointer(proc);
+
+ uint32_t cpsr;
+ if (arm_get_register(proc, ARM_REG_CPSR, &cpsr) < 0)
+ return SWS_FAIL;
+
+ const unsigned thumb_p = BIT(cpsr, 5);
+ arch_addr_t next_pcs[2] = {};
+ if ((thumb_p ? &thumb_get_next_pcs
+ : &arm_get_next_pcs)(proc, pc, next_pcs) < 0)
+ return SWS_FAIL;
+
+ int i;
+ for (i = 0; i < 2; ++i) {
+ /* XXX double cast. */
+ arch_addr_t target
+ = (arch_addr_t)(((uintptr_t)next_pcs[i]) | thumb_p);
+ if (next_pcs[i] != 0 && add_cb(target, add_cb_data) < 0)
+ return SWS_FAIL;
+ }
+
+ debug(1, "PTRACE_CONT");
+ ptrace(PTRACE_CONT, proc->pid, 0, 0);
+ return SWS_OK;
+}
diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c
index fb79e8a..67873ce 100644
--- a/sysdeps/linux-gnu/ia64/regs.c
+++ b/sysdeps/linux-gnu/ia64/regs.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
@@ -77,9 +77,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
return NULL;
return (void *)l;
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- ptrace(PTRACE_POKEUSER, proc->pid, PT_B0, addr);
-}
diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c
index c2fafe1..e25aefb 100644
--- a/sysdeps/linux-gnu/m68k/regs.c
+++ b/sysdeps/linux-gnu/m68k/regs.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
*
* This program is free software; you can redistribute it and/or
@@ -58,9 +59,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
{
return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0);
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
-}
diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c
index 19f97cb..d6a7a50 100644
--- a/sysdeps/linux-gnu/mipsel/regs.c
+++ b/sysdeps/linux-gnu/mipsel/regs.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2008,2009 Juan Cespedes
* Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc.
*
@@ -94,9 +95,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
{
return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0);
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr);
-}
diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c
index ed9b398..40d7e7a 100644
--- a/sysdeps/linux-gnu/ppc/regs.c
+++ b/sysdeps/linux-gnu/ppc/regs.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2002,2008,2009 Juan Cespedes
* Copyright (C) 2009 Juan Cespedes
* Copyright (C) 2008 Luis Machado, IBM Corporation
@@ -63,9 +64,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
{
return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_LNK, 0);
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_LNK, addr);
-}
diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c
index 44e8f67..bb16c61 100644
--- a/sysdeps/linux-gnu/s390/regs.c
+++ b/sysdeps/linux-gnu/s390/regs.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2002,2004,2008,2009 Juan Cespedes
* Copyright (C) 2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
@@ -87,13 +88,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
#endif
return (void *)ret;
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
-#ifdef __s390x__
- if (proc->mask_32bit)
- addr = (void *)((long)addr & PSW_MASK31);
-#endif
- ptrace(PTRACE_POKEUSER, proc->pid, PT_GPR14, addr);
-}
diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c
index 8431c9b..c474c83 100644
--- a/sysdeps/linux-gnu/sparc/regs.c
+++ b/sysdeps/linux-gnu/sparc/regs.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2004,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
@@ -65,12 +66,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
return (void *)a->regs.u_regs[UREG_I6] + 12;
return (void *)a->regs.u_regs[UREG_I6] + 8;
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- proc_archdep *a = (proc_archdep *) (proc->arch_ptr);
- if (!a->valid)
- return;
- ptrace(PTRACE_POKETEXT, proc->pid, a->regs.u_regs[UREG_I6] + 8, addr);
-}
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index e57a5ed..3aea082 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -561,12 +561,12 @@ remove_sw_breakpoints(struct process *proc)
assert(self != NULL);
assert(self->super.on_event == process_stopping_on_event);
- int ct = sizeof(self->sws_bp_addrs) / sizeof(*self->sws_bp_addrs);
+ int ct = sizeof(self->sws_bps) / sizeof(*self->sws_bps);
int i;
for (i = 0; i < ct; ++i)
- if (self->sws_bp_addrs[i] != 0) {
- delete_breakpoint(proc, self->sws_bp_addrs[i]);
- self->sws_bp_addrs[i] = 0;
+ if (self->sws_bps[i] != NULL) {
+ delete_breakpoint(proc, self->sws_bps[i]->addr);
+ self->sws_bps[i] = NULL;
}
}
@@ -586,18 +586,17 @@ sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data)
struct process_stopping_handler *self = data->self;
struct process *proc = self->task_enabling_breakpoint;
- int ct = sizeof(self->sws_bp_addrs)
- / sizeof(*self->sws_bp_addrs);
+ int ct = sizeof(self->sws_bps) / sizeof(*self->sws_bps);
int i;
for (i = 0; i < ct; ++i)
- if (self->sws_bp_addrs[i] == 0) {
- self->sws_bp_addrs[i] = addr;
+ if (self->sws_bps[i] == NULL) {
static struct bp_callbacks cbs = {
.on_hit = sw_singlestep_bp_on_hit,
};
struct breakpoint *bp
= insert_breakpoint(proc, addr, NULL);
breakpoint_set_callbacks(bp, &cbs);
+ self->sws_bps[i] = bp;
return 0;
}
@@ -608,7 +607,9 @@ sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data)
static int
singlestep(struct process_stopping_handler *self)
{
- struct process *proc = self->task_enabling_breakpoint;
+ size_t i;
+ for (i = 0; i < sizeof(self->sws_bps) / sizeof(*self->sws_bps); ++i)
+ self->sws_bps[i] = NULL;
struct sw_singlestep_data data = { self };
switch (arch_sw_singlestep(self->task_enabling_breakpoint,
@@ -617,7 +618,8 @@ singlestep(struct process_stopping_handler *self)
case SWS_HW:
/* Otherwise do the default action: singlestep. */
debug(1, "PTRACE_SINGLESTEP");
- if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) {
+ if (ptrace(PTRACE_SINGLESTEP,
+ self->task_enabling_breakpoint->pid, 0, 0)) {
perror("PTRACE_SINGLESTEP");
return -1;
}
@@ -1038,7 +1040,7 @@ ltrace_exiting_install_handler(struct process *proc)
struct process_vfork_handler
{
struct event_handler super;
- void *bp_addr;
+ int vfork_bp_refd:1;
};
static Event *
@@ -1049,38 +1051,33 @@ process_vfork_on_event(struct event_handler *super, Event *event)
event->proc->pid, event->type);
struct process_vfork_handler *self = (void *)super;
- struct breakpoint *sbp;
+ struct process *proc = event->proc;
assert(self != NULL);
switch (event->type) {
case EVENT_BREAKPOINT:
- /* Remember the vfork return breakpoint. */
- if (self->bp_addr == 0)
- self->bp_addr = event->e_un.brk_addr;
+ /* We turn on the vfork return breakpoint (which
+ * should be the one that we have tripped over just
+ * now) one extra time, so that the vfork parent hits
+ * it as well. */
+ if (!self->vfork_bp_refd) {
+ struct breakpoint *const sbp =
+ dict_find_entry(proc->leader->breakpoints,
+ event->e_un.brk_addr);
+ assert(sbp != NULL);
+ breakpoint_turn_on(sbp, proc->leader);
+ self->vfork_bp_refd = 1;
+ }
break;
case EVENT_EXIT:
case EVENT_EXIT_SIGNAL:
case EVENT_EXEC:
- /* Smuggle back in the vfork return breakpoint, so
- * that our parent can trip over it once again. */
- if (self->bp_addr != 0) {
- sbp = dict_find_entry(event->proc->leader->breakpoints,
- self->bp_addr);
- if (sbp != NULL)
- assert(sbp->libsym == NULL);
- /* We don't mind failing that, it's not a big
- * deal to not display one extra vfork return. */
- insert_breakpoint(event->proc->parent,
- self->bp_addr, NULL);
- }
-
- continue_process(event->proc->parent->pid);
-
/* Remove the leader that we artificially set up
* earlier. */
- change_process_leader(event->proc, event->proc);
- destroy_event_handler(event->proc);
+ change_process_leader(proc, proc);
+ destroy_event_handler(proc);
+ continue_process(proc->parent->pid);
default:
;
diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h
index e988f70..5bb8380 100644
--- a/sysdeps/linux-gnu/trace.h
+++ b/sysdeps/linux-gnu/trace.h
@@ -64,8 +64,8 @@ struct process_stopping_handler
/* The pointer being re-enabled. */
struct breakpoint *breakpoint_being_enabled;
- /* Artificial atomic skip breakpoint, if any needed. */
- arch_addr_t sws_bp_addrs[2];
+ /* Software singlestep breakpoints, if any needed. */
+ struct breakpoint *sws_bps[2];
/* When all tasks are stopped, this callback gets called. */
void (*on_all_stopped)(struct process_stopping_handler *);
diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c
index 3886e84..0a42c6e 100644
--- a/sysdeps/linux-gnu/x86/regs.c
+++ b/sysdeps/linux-gnu/x86/regs.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
@@ -107,11 +107,3 @@ get_return_addr(struct process *proc, void *sp)
ret = conv_32(ret);
return ret;
}
-
-void
-set_return_addr(struct process *proc, void *addr)
-{
- if (proc->e_machine == EM_386)
- addr = (void *)((long int)addr & 0xffffffff);
- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
-}
diff --git a/testsuite/ltrace.torture/Makefile.am b/testsuite/ltrace.torture/Makefile.am
index daa772f..5a45265 100644
--- a/testsuite/ltrace.torture/Makefile.am
+++ b/testsuite/ltrace.torture/Makefile.am
@@ -15,15 +15,9 @@
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
-EXTRA_DIST = \
- ia64-sigill.exp \
- ia64-sigill.s \
- ppc-lwarx.c \
- ppc-lwarx.exp \
- signals.c \
- signals.exp \
- vfork-thread.c \
- vfork-thread.exp
+EXTRA_DIST = arm-singlestep.exp ia64-sigill.exp ia64-sigill.s \
+ ppc-lwarx.c ppc-lwarx.exp signals.c signals.exp \
+ vfork-thread.c vfork-thread.exp
CLEANFILES = *.o *.so *.log *.sum *.ltrace setval.tmp \
signals
diff --git a/testsuite/ltrace.torture/arm-singlestep.exp b/testsuite/ltrace.torture/arm-singlestep.exp
new file mode 100644
index 0000000..0d633d9
--- /dev/null
+++ b/testsuite/ltrace.torture/arm-singlestep.exp
@@ -0,0 +1,44 @@
+# This file is part of ltrace.
+# Copyright (C) 2013 Petr Machata, Red Hat Inc.
+#
+# 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
+
+if {![istarget arm*-*]} {
+ unsupported "arm-specific test"
+ return
+}
+
+set exe [ltraceCompile {} [ltraceSource c {
+ int puc(void) { return 0; }
+
+ int bar(void);
+ int baz(void);
+ __asm__ (" .type bar, %function\n"
+ "bar: \n"
+ " b puc \n"
+ " .type baz, %function\n"
+ "baz: \n"
+ " b puc \n");
+
+ int main(void) { return bar() + baz(); }
+}]]
+
+ltraceMatch [ltraceRun -L -xbar+baz $exe] {
+ {{bar} == 1}
+ {{baz} == 1}
+}
+
+ltraceDone