Petr Machata 4b23d2c
diff --git a/Makefile.am b/Makefile.am
Petr Machata 4b23d2c
index c3356de..141ff85 100644
Petr Machata 4b23d2c
--- a/Makefile.am
Petr Machata 4b23d2c
+++ b/Makefile.am
Petr Machata 4b23d2c
@@ -1,5 +1,5 @@
Petr Machata 4b23d2c
 # This file is part of ltrace.
Petr Machata 4b23d2c
-# Copyright (C) 2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
 # Copyright (C) 2010 Marc Kleine-Budde, Pengutronix
Petr Machata 4b23d2c
 # Copyright (C) 2010 Zachary T Welch, CodeSourcery
Petr Machata 4b23d2c
 #
Petr Machata 4b23d2c
@@ -33,6 +33,7 @@ noinst_LTLIBRARIES = \
Petr Machata 4b23d2c
 	libltrace.la
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 libltrace_la_SOURCES = \
Petr Machata 4b23d2c
+	bits.c \
Petr Machata 4b23d2c
 	breakpoints.c \
Petr Machata 4b23d2c
 	debug.c \
Petr Machata 4b23d2c
 	demangle.c \
Petr Machata 4b23d2c
@@ -83,6 +84,7 @@ ltrace_LDADD = \
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 noinst_HEADERS = \
Petr Machata 4b23d2c
+	bits.h \
Petr Machata 4b23d2c
 	backend.h \
Petr Machata 4b23d2c
 	breakpoint.h \
Petr Machata 4b23d2c
 	common.h \
Petr Machata cb74839
diff --git a/README b/README
Petr Machata cb74839
index 3db5bc8..95871d1 100644
Petr Machata cb74839
--- a/README
Petr Machata cb74839
+++ b/README
Petr Machata cb74839
@@ -24,6 +24,8 @@ The following targets are currently (at least somewhat) supported.
Petr Machata cb74839
 Some of them may be more or less broken in reality, it is not feasible
Petr Machata cb74839
 to test each release comprehensively on each target.
Petr Machata cb74839
 
Petr Machata cb74839
+	armv6l-*-linux-gnueabi
Petr Machata cb74839
+	armv7l-*-linux-gnueabihf
Petr Machata cb74839
 	i[4567]86-*-linux-gnu
Petr Machata cb74839
 	ia64-*-linux-gnu
Petr Machata cb74839
 	m68k-*-linux-gnu
Petr Machata cb74839
@@ -41,11 +43,6 @@ current status is unknown:
Petr Machata cb74839
 	sparc64*-*-linux-gnu
Petr Machata cb74839
 	alpha*-*-linux-gnu
Petr Machata cb74839
 
Petr Machata cb74839
-Support of the following systems is known to be broken and requires
Petr Machata cb74839
-fixing:
Petr Machata cb74839
-
Petr Machata cb74839
-	arm-*-linux-gnueabi
Petr Machata cb74839
-
Petr Machata cb74839
 
Petr Machata cb74839
 Bug Reports
Petr Machata cb74839
 -----------
Petr Machata cb74839
@@ -83,7 +80,7 @@ quick one-liner), it is advisable to send an e-mail beforehand.
Petr Machata cb74839
 
Petr Machata cb74839
 
Petr Machata cb74839
 -------------------------------------------------------------------------------
Petr Machata cb74839
-Copyright (C) 2012 Petr Machata <pmachata@redhat.com>
Petr Machata cb74839
+Copyright (C) 2012,2013 Petr Machata <pmachata@redhat.com>
Petr Machata cb74839
 Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>
Petr Machata cb74839
 This file is part of ltrace.
Petr Machata cb74839
 
Petr Machata 4b23d2c
diff --git a/backend.h b/backend.h
Petr Machata 4b23d2c
index cfac65e..a9de3b4 100644
Petr Machata 4b23d2c
--- a/backend.h
Petr Machata 4b23d2c
+++ b/backend.h
Petr Machata 4b23d2c
@@ -107,10 +107,6 @@ void *get_stack_pointer(struct process *proc);
Petr Machata 4b23d2c
  * function returns.  */
Petr Machata 4b23d2c
 void *get_return_addr(struct process *proc, void *stack_pointer);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-/* Adjust PROC so that when the current function returns, it returns
Petr Machata 4b23d2c
- * to ADDR.  */
Petr Machata 4b23d2c
-void set_return_addr(struct process *proc, void *addr);
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
 /* Enable breakpoint SBP in process PROC.  */
Petr Machata 4b23d2c
 void enable_breakpoint(struct process *proc, struct breakpoint *sbp);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
diff --git a/bits.c b/bits.c
Petr Machata 4b23d2c
new file mode 100644
Petr Machata 4b23d2c
index 0000000..bde2e71
Petr Machata 4b23d2c
--- /dev/null
Petr Machata 4b23d2c
+++ b/bits.c
Petr Machata 4b23d2c
@@ -0,0 +1,34 @@
Petr Machata 4b23d2c
+/*
Petr Machata 4b23d2c
+ * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
+ * modify it under the terms of the GNU General Public License as
Petr Machata 4b23d2c
+ * published by the Free Software Foundation; either version 2 of the
Petr Machata 4b23d2c
+ * License, or (at your option) any later version.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * This program is distributed in the hope that it will be useful, but
Petr Machata 4b23d2c
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
Petr Machata 4b23d2c
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Petr Machata 4b23d2c
+ * General Public License for more details.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * You should have received a copy of the GNU General Public License
Petr Machata 4b23d2c
+ * along with this program; if not, write to the Free Software
Petr Machata 4b23d2c
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Petr Machata 4b23d2c
+ * 02110-1301 USA
Petr Machata 4b23d2c
+ */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+#include "bits.h"
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+/* This is called rarely, and any overhead will be lost in ptrace
Petr Machata 4b23d2c
+ * noise, so the algorithm doesn't need to be terribly clever.  For
Petr Machata 4b23d2c
+ * the same reason we don't bother defining the corresponding _32
Petr Machata 4b23d2c
+ * variant.  */
Petr Machata 4b23d2c
+unsigned
Petr Machata 4b23d2c
+bitcount(uint64_t u)
Petr Machata 4b23d2c
+{
Petr Machata 4b23d2c
+	int c = 0;
Petr Machata 4b23d2c
+	for (; u > 0; u &= u - 1)
Petr Machata 4b23d2c
+		c++;
Petr Machata 4b23d2c
+	return c;
Petr Machata 4b23d2c
+}
Petr Machata 4b23d2c
diff --git a/bits.h b/bits.h
Petr Machata 4b23d2c
new file mode 100644
Petr Machata 4b23d2c
index 0000000..7dbe478
Petr Machata 4b23d2c
--- /dev/null
Petr Machata 4b23d2c
+++ b/bits.h
Petr Machata 4b23d2c
@@ -0,0 +1,29 @@
Petr Machata 4b23d2c
+/*
Petr Machata 4b23d2c
+ * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
+ * modify it under the terms of the GNU General Public License as
Petr Machata 4b23d2c
+ * published by the Free Software Foundation; either version 2 of the
Petr Machata 4b23d2c
+ * License, or (at your option) any later version.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * This program is distributed in the hope that it will be useful, but
Petr Machata 4b23d2c
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
Petr Machata 4b23d2c
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Petr Machata 4b23d2c
+ * General Public License for more details.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * You should have received a copy of the GNU General Public License
Petr Machata 4b23d2c
+ * along with this program; if not, write to the Free Software
Petr Machata 4b23d2c
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Petr Machata 4b23d2c
+ * 02110-1301 USA
Petr Machata 4b23d2c
+ */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+#ifndef _BITS_H_
Petr Machata 4b23d2c
+#define _BITS_H_
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+#include <stdint.h>
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+/* Count bits in U that are 1.  */
Petr Machata 4b23d2c
+unsigned bitcount(uint64_t u);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+#endif /* _BITS_H_ */
Petr Machata 4b23d2c
diff --git a/breakpoint.h b/breakpoint.h
Petr Machata 4b23d2c
index 18af7a9..963cc66 100644
Petr Machata 4b23d2c
--- a/breakpoint.h
Petr Machata 4b23d2c
+++ b/breakpoint.h
Petr Machata 4b23d2c
@@ -1,6 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2009 Juan Cespedes
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
  * This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
@@ -82,11 +82,10 @@ int breakpoint_init(struct breakpoint *bp, struct process *proc,
Petr Machata 4b23d2c
 		    arch_addr_t addr, struct library_symbol *libsym);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 /* Make a clone of breakpoint BP into the area of memory pointed to by
Petr Machata 4b23d2c
- * RETP.  The original breakpoint was assigned to process OLD_PROC,
Petr Machata 4b23d2c
- * the cloned breakpoint will be attached to process NEW_PROC.
Petr Machata 4b23d2c
+ * RETP.  Symbols of cloned breakpoint are looked up in NEW_PROC.
Petr Machata 4b23d2c
  * Returns 0 on success or a negative value on failure.  */
Petr Machata 4b23d2c
 int breakpoint_clone(struct breakpoint *retp, struct process *new_proc,
Petr Machata 4b23d2c
-		     struct breakpoint *bp, struct process *old_proc);
Petr Machata 4b23d2c
+		     struct breakpoint *bp);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 /* Set callbacks.  If CBS is non-NULL, then BP->cbs shall be NULL.  */
Petr Machata 4b23d2c
 void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs);
Petr Machata 4b23d2c
diff --git a/breakpoints.c b/breakpoints.c
Petr Machata 4b23d2c
index 8db4e26..7b5530a 100644
Petr Machata 4b23d2c
--- a/breakpoints.c
Petr Machata 4b23d2c
+++ b/breakpoints.c
Petr Machata 4b23d2c
@@ -1,6 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
- * Copyright (C) 2006,2007,2011,2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ * Copyright (C) 2006,2007,2011,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 1998,2001,2002,2003,2007,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Ian Wienand
Petr Machata 4b23d2c
@@ -117,7 +117,7 @@ arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp)
Petr Machata 4b23d2c
 #endif
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 static void
Petr Machata 4b23d2c
-breakpoint_init_base(struct breakpoint *bp, struct process *proc,
Petr Machata 4b23d2c
+breakpoint_init_base(struct breakpoint *bp,
Petr Machata 4b23d2c
 		     arch_addr_t addr, struct library_symbol *libsym)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	bp->cbs = NULL;
Petr Machata 4b23d2c
@@ -135,7 +135,7 @@ int
Petr Machata 4b23d2c
 breakpoint_init(struct breakpoint *bp, struct process *proc,
Petr Machata 4b23d2c
 		arch_addr_t addr, struct library_symbol *libsym)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
-	breakpoint_init_base(bp, proc, addr, libsym);
Petr Machata 4b23d2c
+	breakpoint_init_base(bp, addr, libsym);
Petr Machata 4b23d2c
 	return arch_breakpoint_init(proc, bp);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
@@ -157,7 +157,7 @@ breakpoint_destroy(struct breakpoint *bp)
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 int
Petr Machata 4b23d2c
 breakpoint_clone(struct breakpoint *retp, struct process *new_proc,
Petr Machata 4b23d2c
-		 struct breakpoint *bp, struct process *old_proc)
Petr Machata 4b23d2c
+		 struct breakpoint *bp)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	struct library_symbol *libsym = NULL;
Petr Machata 4b23d2c
 	if (bp->libsym != NULL) {
Petr Machata 4b23d2c
@@ -165,7 +165,7 @@ breakpoint_clone(struct breakpoint *retp, struct process *new_proc,
Petr Machata 4b23d2c
 		assert(rc == 0);
Petr Machata 4b23d2c
 	}
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-	breakpoint_init_base(retp, new_proc, bp->addr, libsym);
Petr Machata 4b23d2c
+	breakpoint_init_base(retp, bp->addr, libsym);
Petr Machata 4b23d2c
 	memcpy(retp->orig_value, bp->orig_value, sizeof(bp->orig_value));
Petr Machata 4b23d2c
 	retp->enabled = bp->enabled;
Petr Machata 4b23d2c
 	if (arch_breakpoint_clone(retp, bp) < 0)
Petr Machata 4b23d2c
@@ -211,6 +211,22 @@ insert_breakpoint(struct process *proc, void *addr,
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	assert(addr != 0);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
+	/* We first create the breakpoint to find out what it's real
Petr Machata 4b23d2c
+	 * address is.  This makes a difference on ARM.
Petr Machata 4b23d2c
+	 *
Petr Machata 4b23d2c
+	 * XXX The real problem here is that to create a return
Petr Machata 4b23d2c
+	 * breakpoint ltrace calls get_return_addr and then
Petr Machata 4b23d2c
+	 * insert_breakpoint.  So get_return_addr needs to encode all
Petr Machata 4b23d2c
+	 * the information necessary for breakpoint_init into the
Petr Machata 4b23d2c
+	 * address itself, so ADDR is potentially mangled.  We filter
Petr Machata 4b23d2c
+	 * the noise out by first creating the breakpoint on stack,
Petr Machata 4b23d2c
+	 * and then looking at the address of the created breakpoint.
Petr Machata 4b23d2c
+	 * Replacing get_return_addr with get_return_breakpoint might
Petr Machata 4b23d2c
+	 * be a better solution.  */
Petr Machata 4b23d2c
+	struct breakpoint bp;
Petr Machata 4b23d2c
+	if (breakpoint_init(&bp, proc, addr, libsym) < 0)
Petr Machata 4b23d2c
+		return NULL;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
 	/* XXX what we need to do instead is have a list of
Petr Machata 4b23d2c
 	 * breakpoints that are enabled at this address.  The
Petr Machata 4b23d2c
 	 * following works if every breakpoint is the same and there's
Petr Machata 4b23d2c
@@ -218,20 +234,21 @@ insert_breakpoint(struct process *proc, void *addr,
Petr Machata 4b23d2c
 	 * will suffice, about the only realistic case where we need
Petr Machata 4b23d2c
 	 * to have more than one breakpoint per address is return from
Petr Machata 4b23d2c
 	 * a recursive library call.  */
Petr Machata 4b23d2c
-	struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
Petr Machata 4b23d2c
-	if (sbp == NULL) {
Petr Machata 4b23d2c
+	struct breakpoint *sbp = dict_find_entry(leader->breakpoints, bp.addr);
Petr Machata 4b23d2c
+	if (sbp != NULL) {
Petr Machata 4b23d2c
+		breakpoint_destroy(&bp);
Petr Machata 4b23d2c
+	} else {
Petr Machata 4b23d2c
+	  //fprintf(stderr, "new BP at %p\n", addr);
Petr Machata 4b23d2c
 		sbp = malloc(sizeof(*sbp));
Petr Machata 4b23d2c
-		if (sbp == NULL
Petr Machata 4b23d2c
-		    || breakpoint_init(sbp, proc, addr, libsym) < 0) {
Petr Machata 4b23d2c
-			free(sbp);
Petr Machata 4b23d2c
-			return NULL;
Petr Machata 4b23d2c
-		}
Petr Machata 4b23d2c
-		if (proc_add_breakpoint(leader, sbp) < 0) {
Petr Machata 4b23d2c
+		if (sbp == NULL) {
Petr Machata 4b23d2c
 		fail:
Petr Machata 4b23d2c
-			breakpoint_destroy(sbp);
Petr Machata 4b23d2c
 			free(sbp);
Petr Machata 4b23d2c
+			breakpoint_destroy(&bp);
Petr Machata 4b23d2c
 			return NULL;
Petr Machata 4b23d2c
 		}
Petr Machata 4b23d2c
+		memcpy(sbp, &bp, sizeof(*sbp));
Petr Machata 4b23d2c
+		if (proc_add_breakpoint(leader, sbp) < 0)
Petr Machata 4b23d2c
+			goto fail;
Petr Machata 4b23d2c
 	}
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	if (breakpoint_turn_on(sbp, proc) < 0) {
Petr Machata 4b23d2c
diff --git a/handle_event.c b/handle_event.c
Petr Machata 4b23d2c
index 9dbb696..1eaea09 100644
Petr Machata 4b23d2c
--- a/handle_event.c
Petr Machata 4b23d2c
+++ b/handle_event.c
Petr Machata 4b23d2c
@@ -607,7 +607,6 @@ handle_breakpoint(Event *event)
Petr Machata 4b23d2c
 					calc_time_spent(event->proc);
Petr Machata 4b23d2c
 				}
Petr Machata 4b23d2c
 			}
Petr Machata 4b23d2c
-			event->proc->return_addr = brk_addr;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 			struct library_symbol *libsym =
Petr Machata 4b23d2c
 			    event->proc->callstack[i].c_un.libfunc;
Petr Machata 4b23d2c
@@ -663,8 +662,6 @@ handle_breakpoint(Event *event)
Petr Machata 4b23d2c
 		if (event->proc->state != STATE_IGNORED
Petr Machata 4b23d2c
 		    && sbp->libsym != NULL) {
Petr Machata 4b23d2c
 			event->proc->stack_pointer = get_stack_pointer(event->proc);
Petr Machata 4b23d2c
-			event->proc->return_addr =
Petr Machata 4b23d2c
-				get_return_addr(event->proc, event->proc->stack_pointer);
Petr Machata 4b23d2c
 			callstack_push_symfunc(event->proc, sbp->libsym);
Petr Machata 4b23d2c
 			output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym);
Petr Machata 4b23d2c
 		}
Petr Machata 4b23d2c
@@ -722,9 +719,11 @@ callstack_push_symfunc(struct process *proc, struct library_symbol *sym)
Petr Machata 4b23d2c
 	elem->is_syscall = 0;
Petr Machata 4b23d2c
 	elem->c_un.libfunc = sym;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-	elem->return_addr = proc->return_addr;
Petr Machata 4b23d2c
-	if (elem->return_addr)
Petr Machata 4b23d2c
-		insert_breakpoint(proc, elem->return_addr, NULL);
Petr Machata 4b23d2c
+	arch_addr_t return_addr = get_return_addr(proc, proc->stack_pointer);
Petr Machata 4b23d2c
+	struct breakpoint *rbp = NULL;
Petr Machata 4b23d2c
+	if (return_addr != 0)
Petr Machata 4b23d2c
+		rbp = insert_breakpoint(proc, return_addr, NULL);
Petr Machata 4b23d2c
+	elem->return_addr = rbp != NULL ? rbp->addr : 0;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	if (opt_T || options.summary) {
Petr Machata 4b23d2c
 		struct timezone tz;
Petr Machata 4b23d2c
diff --git a/lens_default.c b/lens_default.c
Petr Machata 4b23d2c
index ed3d0e1..47b8c70 100644
Petr Machata 4b23d2c
--- a/lens_default.c
Petr Machata 4b23d2c
+++ b/lens_default.c
Petr Machata 4b23d2c
@@ -29,6 +29,7 @@
Petr Machata 4b23d2c
 #include <stdio.h>
Petr Machata 4b23d2c
 #include <string.h>
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
+#include "bits.h"
Petr Machata 4b23d2c
 #include "proc.h"
Petr Machata 4b23d2c
 #include "lens_default.h"
Petr Machata 4b23d2c
 #include "value.h"
Petr Machata 4b23d2c
@@ -608,15 +609,6 @@ out_bits(FILE *stream, size_t low, size_t high)
Petr Machata 4b23d2c
 		return fprintf(stream, "%zd-%zd", low, high);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-static unsigned
Petr Machata 4b23d2c
-bitcount(unsigned u)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	int c = 0;
Petr Machata 4b23d2c
-	for (; u > 0; u &= u - 1)
Petr Machata 4b23d2c
-		c++;
Petr Machata 4b23d2c
-	return c;
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
 static int
Petr Machata 4b23d2c
 bitvect_lens_format_cb(struct lens *lens, FILE *stream,
Petr Machata 4b23d2c
 		       struct value *value, struct value_dict *arguments)
Petr Machata cb74839
diff --git a/ltrace-elf.c b/ltrace-elf.c
Petr Machata cb74839
index 1d0f769..af25f8f 100644
Petr Machata cb74839
--- a/ltrace-elf.c
Petr Machata cb74839
+++ b/ltrace-elf.c
Petr Machata cb74839
@@ -1,6 +1,6 @@
Petr Machata cb74839
 /*
Petr Machata cb74839
  * This file is part of ltrace.
Petr Machata cb74839
- * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc.
Petr Machata cb74839
+ * Copyright (C) 2006,2010,2011,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata cb74839
  * Copyright (C) 2010 Zachary T Welch, CodeSourcery
Petr Machata cb74839
  * Copyright (C) 2010 Joe Damato
Petr Machata cb74839
  * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes
Petr Machata cb74839
@@ -141,8 +141,9 @@ elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr,
Petr Machata cb74839
 			return 0;
Petr Machata cb74839
 		}
Petr Machata cb74839
 	}
Petr Machata cb74839
-	return -1;
Petr Machata cb74839
 
Petr Machata cb74839
+	*tgt_sec = NULL;
Petr Machata cb74839
+	return 0;
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
 static int
Petr Machata cb74839
@@ -203,23 +204,23 @@ elf_get_section_named(struct ltelf *lte, const char *name,
Petr Machata cb74839
 				  &name_p, &data);
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
-static int
Petr Machata cb74839
-need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
Petr Machata cb74839
+int
Petr Machata cb74839
+elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
Petr Machata cb74839
 {
Petr Machata cb74839
 	assert(data != NULL);
Petr Machata cb74839
 	if (data->d_size < size || offset > data->d_size - size) {
Petr Machata cb74839
 		debug(1, "Not enough data to read %"PRId64"-byte value"
Petr Machata cb74839
 		      " at offset %"PRId64".", size, offset);
Petr Machata cb74839
-		return -1;
Petr Machata cb74839
+		return 0;
Petr Machata cb74839
 	}
Petr Machata cb74839
-	return 0;
Petr Machata cb74839
+	return 1;
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
 #define DEF_READER(NAME, SIZE)						\
Petr Machata cb74839
 	int								\
Petr Machata cb74839
 	NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp)	\
Petr Machata cb74839
 	{								\
Petr Machata cb74839
-		if (!need_data(data, offset, SIZE / 8) < 0)		\
Petr Machata cb74839
+		if (!elf_can_read_next(data, offset, SIZE / 8))		\
Petr Machata cb74839
 			return -1;					\
Petr Machata cb74839
 									\
Petr Machata cb74839
 		if (data->d_buf == NULL) /* NODATA section */ {		\
Petr Machata cb74839
@@ -236,12 +237,63 @@ need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
Petr Machata cb74839
 		return 0;						\
Petr Machata cb74839
 	}
Petr Machata cb74839
 
Petr Machata cb74839
+DEF_READER(elf_read_u8, 8)
Petr Machata cb74839
 DEF_READER(elf_read_u16, 16)
Petr Machata cb74839
 DEF_READER(elf_read_u32, 32)
Petr Machata cb74839
 DEF_READER(elf_read_u64, 64)
Petr Machata cb74839
 
Petr Machata cb74839
 #undef DEF_READER
Petr Machata cb74839
 
Petr Machata cb74839
+#define DEF_READER(NAME, SIZE)						\
Petr Machata cb74839
+	int								\
Petr Machata cb74839
+	NAME(Elf_Data *data, GElf_Xword *offset, uint##SIZE##_t *retp)	\
Petr Machata cb74839
+	{								\
Petr Machata cb74839
+		int rc = elf_read_u##SIZE(data, *offset, retp);		\
Petr Machata cb74839
+		if (rc < 0)						\
Petr Machata cb74839
+			return rc;					\
Petr Machata cb74839
+		*offset += SIZE / 8;					\
Petr Machata cb74839
+		return 0;						\
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+DEF_READER(elf_read_next_u8, 8)
Petr Machata cb74839
+DEF_READER(elf_read_next_u16, 16)
Petr Machata cb74839
+DEF_READER(elf_read_next_u32, 32)
Petr Machata cb74839
+DEF_READER(elf_read_next_u64, 64)
Petr Machata cb74839
+
Petr Machata cb74839
+#undef DEF_READER
Petr Machata cb74839
+
Petr Machata cb74839
+int
Petr Machata cb74839
+elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp)
Petr Machata cb74839
+{
Petr Machata cb74839
+	uint64_t result = 0;
Petr Machata cb74839
+	int shift = 0;
Petr Machata cb74839
+	int size = 8 * sizeof result;
Petr Machata cb74839
+
Petr Machata cb74839
+	while (1) {
Petr Machata cb74839
+		uint8_t byte;
Petr Machata cb74839
+		if (elf_read_next_u8(data, offset, &byte) < 0)
Petr Machata cb74839
+			return -1;
Petr Machata cb74839
+
Petr Machata cb74839
+		uint8_t payload = byte & 0x7f;
Petr Machata cb74839
+		result |= (uint64_t)payload << shift;
Petr Machata cb74839
+		shift += 7;
Petr Machata cb74839
+		if (shift > size && byte != 0x1)
Petr Machata cb74839
+			return -1;
Petr Machata cb74839
+		if ((byte & 0x80) == 0)
Petr Machata cb74839
+			break;
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	if (retp != NULL)
Petr Machata cb74839
+		*retp = result;
Petr Machata cb74839
+	return 0;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+int
Petr Machata cb74839
+elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp)
Petr Machata cb74839
+{
Petr Machata cb74839
+	return elf_read_next_uleb128(data, &offset, retp);
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
 int
Petr Machata cb74839
 open_elf(struct ltelf *lte, const char *filename)
Petr Machata cb74839
 {
Petr Machata cb74839
diff --git a/ltrace-elf.h b/ltrace-elf.h
Petr Machata cb74839
index b76d1eb..178258b 100644
Petr Machata cb74839
--- a/ltrace-elf.h
Petr Machata cb74839
+++ b/ltrace-elf.h
Petr Machata cb74839
@@ -1,6 +1,6 @@
Petr Machata cb74839
 /*
Petr Machata cb74839
  * This file is part of ltrace.
Petr Machata cb74839
- * Copyright (C) 2006,2010,2012 Petr Machata, Red Hat Inc.
Petr Machata cb74839
+ * Copyright (C) 2006,2010,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata cb74839
  * Copyright (C) 2010 Zachary T Welch
Petr Machata cb74839
  * Copyright (C) 2001,2004,2007,2009 Juan Cespedes
Petr Machata cb74839
  * Copyright (C) 2006 Ian Wienand
Petr Machata cb74839
@@ -95,6 +95,12 @@ int elf_get_sym_info(struct ltelf *lte, const char *filename,
Petr Machata cb74839
 		     size_t sym_index, GElf_Rela *rela, GElf_Sym *sym);
Petr Machata cb74839
 
Petr Machata cb74839
 Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr);
Petr Machata cb74839
+
Petr Machata cb74839
+/* The following three look for sections based on various criteria.
Petr Machata cb74839
+ * They return 0 if there was no error, or a negative value if there
Petr Machata cb74839
+ * was.  If the section was found, it is returned in *TGT_SEC, and the
Petr Machata cb74839
+ * header is stored te TGT_SHDR.  If it wasn't found, *TGT_SEC is set
Petr Machata cb74839
+ * to NULL.  */
Petr Machata cb74839
 int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr,
Petr Machata cb74839
 			     Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr);
Petr Machata cb74839
 int elf_get_section_type(struct ltelf *lte, GElf_Word type,
Petr Machata cb74839
@@ -102,13 +108,29 @@ int elf_get_section_type(struct ltelf *lte, GElf_Word type,
Petr Machata cb74839
 int elf_get_section_named(struct ltelf *lte, const char *name,
Petr Machata cb74839
 			  Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr);
Petr Machata cb74839
 
Petr Machata cb74839
-/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET,
Petr Machata cb74839
- * and store it in *RETP.  Returns 0 on success or a negative value if
Petr Machata cb74839
- * there's not enough data.  */
Petr Machata cb74839
+/* Read, respectively, 1, 2, 4, or 8 bytes from Elf data at given
Petr Machata cb74839
+ * OFFSET, and store it in *RETP.  Returns 0 on success or a negative
Petr Machata cb74839
+ * value if there's not enough data.  */
Petr Machata cb74839
+int elf_read_u8(Elf_Data *data, GElf_Xword offset, uint8_t *retp);
Petr Machata cb74839
 int elf_read_u16(Elf_Data *data, GElf_Xword offset, uint16_t *retp);
Petr Machata cb74839
 int elf_read_u32(Elf_Data *data, GElf_Xword offset, uint32_t *retp);
Petr Machata cb74839
 int elf_read_u64(Elf_Data *data, GElf_Xword offset, uint64_t *retp);
Petr Machata cb74839
 
Petr Machata cb74839
+/* Read at most 64-bit quantity recorded in an ULEB128 variable-length
Petr Machata cb74839
+ * encoding.  */
Petr Machata cb74839
+int elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp);
Petr Machata cb74839
+
Petr Machata cb74839
+/* These are same as above, but update *OFFSET with the width
Petr Machata cb74839
+ * of read datum.  */
Petr Machata cb74839
+int elf_read_next_u8(Elf_Data *data, GElf_Xword *offset, uint8_t *retp);
Petr Machata cb74839
+int elf_read_next_u16(Elf_Data *data, GElf_Xword *offset, uint16_t *retp);
Petr Machata cb74839
+int elf_read_next_u32(Elf_Data *data, GElf_Xword *offset, uint32_t *retp);
Petr Machata cb74839
+int elf_read_next_u64(Elf_Data *data, GElf_Xword *offset, uint64_t *retp);
Petr Machata cb74839
+int elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp);
Petr Machata cb74839
+
Petr Machata cb74839
+/* Return whether there's AMOUNT more bytes after OFFSET in DATA.  */
Petr Machata cb74839
+int elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword amount);
Petr Machata cb74839
+
Petr Machata cb74839
 #if __WORDSIZE == 32
Petr Machata cb74839
 #define PRI_ELF_ADDR		PRIx32
Petr Machata cb74839
 #define GELF_ADDR_CAST(x)	(void *)(uint32_t)(x)
Petr Machata 4b23d2c
diff --git a/output.c b/output.c
Petr Machata 4b23d2c
index fe62bb4..f046df8 100644
Petr Machata 4b23d2c
--- a/output.c
Petr Machata 4b23d2c
+++ b/output.c
Petr Machata 4b23d2c
@@ -1,6 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2010 Joe Damato
Petr Machata 4b23d2c
  * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Paul Gilliam, IBM Corporation
Petr Machata 4b23d2c
@@ -119,12 +119,15 @@ begin_of_line(struct process *proc, int is_func, int indent)
Petr Machata 4b23d2c
 		}
Petr Machata 4b23d2c
 	}
Petr Machata 4b23d2c
 	if (opt_i) {
Petr Machata 4b23d2c
-		if (is_func)
Petr Machata 4b23d2c
+		if (is_func) {
Petr Machata 4b23d2c
+			struct callstack_element *stel
Petr Machata 4b23d2c
+				= &proc->callstack[proc->callstack_depth - 1];
Petr Machata 4b23d2c
 			current_column += fprintf(options.output, "[%p] ",
Petr Machata 4b23d2c
-						  proc->return_addr);
Petr Machata 4b23d2c
-		else
Petr Machata 4b23d2c
+						  stel->return_addr);
Petr Machata 4b23d2c
+		} else {
Petr Machata 4b23d2c
 			current_column += fprintf(options.output, "[%p] ",
Petr Machata 4b23d2c
 						  proc->instruction_pointer);
Petr Machata 4b23d2c
+		}
Petr Machata 4b23d2c
 	}
Petr Machata 4b23d2c
 	if (options.indent > 0 && indent) {
Petr Machata 4b23d2c
 		output_indent(proc);
Petr Machata 4b23d2c
diff --git a/proc.c b/proc.c
Petr Machata 4b23d2c
index db3f645..7dfde7c 100644
Petr Machata 4b23d2c
--- a/proc.c
Petr Machata 4b23d2c
+++ b/proc.c
Petr Machata 4b23d2c
@@ -314,8 +314,7 @@ clone_single_bp(void *key, void *value, void *u)
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	struct breakpoint *clone = malloc(sizeof(*clone));
Petr Machata 4b23d2c
 	if (clone == NULL
Petr Machata 4b23d2c
-	    || breakpoint_clone(clone, data->new_proc,
Petr Machata 4b23d2c
-				bp, data->old_proc) < 0) {
Petr Machata 4b23d2c
+	    || breakpoint_clone(clone, data->new_proc, bp) < 0) {
Petr Machata 4b23d2c
 	fail:
Petr Machata 4b23d2c
 		free(clone);
Petr Machata 4b23d2c
 		data->error = -1;
Petr Machata 4b23d2c
@@ -1050,6 +1049,7 @@ proc_each_symbol(struct process *proc, struct library_symbol *start_after,
Petr Machata 4b23d2c
 		return 0;						\
Petr Machata 4b23d2c
 	}
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
+DEF_READER(proc_read_8, 8)
Petr Machata 4b23d2c
 DEF_READER(proc_read_16, 16)
Petr Machata 4b23d2c
 DEF_READER(proc_read_32, 32)
Petr Machata 4b23d2c
 DEF_READER(proc_read_64, 64)
Petr Machata 4b23d2c
diff --git a/proc.h b/proc.h
Petr Machata 4b23d2c
index 04c0ef7..03708dc 100644
Petr Machata 4b23d2c
--- a/proc.h
Petr Machata 4b23d2c
+++ b/proc.h
Petr Machata 4b23d2c
@@ -65,7 +65,7 @@ struct callstack_element {
Petr Machata 4b23d2c
 		struct library_symbol * libfunc;
Petr Machata 4b23d2c
 	} c_un;
Petr Machata 4b23d2c
 	int is_syscall;
Petr Machata 4b23d2c
-	void * return_addr;
Petr Machata 4b23d2c
+	arch_addr_t return_addr;
Petr Machata 4b23d2c
 	struct timeval time_spent;
Petr Machata 4b23d2c
 	struct fetch_context *fetch_context;
Petr Machata 4b23d2c
 	struct value_dict *arguments;
Petr Machata 4b23d2c
@@ -106,7 +106,6 @@ struct process {
Petr Machata 4b23d2c
 	/* Arch-dependent: */
Petr Machata 4b23d2c
 	void * instruction_pointer;
Petr Machata 4b23d2c
 	void * stack_pointer;      /* To get return addr, args... */
Petr Machata 4b23d2c
-	void * return_addr;
Petr Machata 4b23d2c
 	void * arch_ptr;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	/* XXX We would like to replace this with a pointer to ABI
Petr Machata 4b23d2c
@@ -116,11 +115,6 @@ struct process {
Petr Machata 4b23d2c
 	short e_machine;
Petr Machata 4b23d2c
 	char e_class;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-	/* XXX this shoudl go to ARM's arch_process_data.  */
Petr Machata 4b23d2c
-#ifdef __arm__
Petr Machata 4b23d2c
-	int thumb_mode;           /* ARM execution mode: 0: ARM, 1: Thumb */
Petr Machata 4b23d2c
-#endif
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
 #if defined(HAVE_LIBUNWIND)
Petr Machata 4b23d2c
 	/* libunwind address space */
Petr Machata 4b23d2c
 	unw_addr_space_t unwind_as;
Petr Machata 4b23d2c
@@ -254,10 +248,11 @@ struct library_symbol *proc_each_symbol
Petr Machata 4b23d2c
 	 enum callback_status (*cb)(struct library_symbol *, void *),
Petr Machata 4b23d2c
 	 void *data);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-/* Read 16, 32 or 64-bit quantity located at ADDR in PROC.  The
Petr Machata 4b23d2c
+/* Read 8, 16, 32 or 64-bit quantity located at ADDR in PROC.  The
Petr Machata 4b23d2c
  * resulting value is stored in *LP.  0 is returned on success or a
Petr Machata 4b23d2c
  * negative value on failure.  This uses umovebytes under the hood
Petr Machata 4b23d2c
  * (see backend.h).  */
Petr Machata 4b23d2c
+int proc_read_8(struct process *proc, arch_addr_t addr, uint8_t *lp);
Petr Machata 4b23d2c
 int proc_read_16(struct process *proc, arch_addr_t addr, uint16_t *lp);
Petr Machata 4b23d2c
 int proc_read_32(struct process *proc, arch_addr_t addr, uint32_t *lp);
Petr Machata 4b23d2c
 int proc_read_64(struct process *proc, arch_addr_t addr, uint64_t *lp);
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c
Petr Machata 4b23d2c
index c197225..9ccd8f2 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/alpha/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/alpha/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
  * This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
@@ -58,9 +59,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 26 /* RA */ , 0);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr);
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/arm/Makefile.am b/sysdeps/linux-gnu/arm/Makefile.am
Petr Machata cb74839
index 385424c..2c180c6 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/arm/Makefile.am
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/arm/Makefile.am
Petr Machata 4b23d2c
@@ -1,4 +1,5 @@
Petr Machata 4b23d2c
 # This file is part of ltrace.
Petr Machata 4b23d2c
+# Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
 # Copyright (C) 2010 Marc Kleine-Budde, Pengutronix
Petr Machata 4b23d2c
 #
Petr Machata 4b23d2c
 # This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
@@ -16,21 +17,11 @@
Petr Machata 4b23d2c
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Petr Machata 4b23d2c
 # 02110-1301 USA
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-noinst_LTLIBRARIES = \
Petr Machata 4b23d2c
-	../libcpu.la
Petr Machata 4b23d2c
+noinst_LTLIBRARIES = ../libcpu.la
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-___libcpu_la_SOURCES = \
Petr Machata 4b23d2c
-	breakpoint.c \
Petr Machata 4b23d2c
-	plt.c \
Petr Machata 4b23d2c
-	regs.c \
Petr Machata 4b23d2c
-	trace.c
Petr Machata cb74839
+___libcpu_la_SOURCES = breakpoint.c fetch.c plt.c regs.c trace.c
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-noinst_HEADERS = \
Petr Machata 4b23d2c
-	arch.h \
Petr Machata 4b23d2c
-	arch_syscallent.h \
Petr Machata 4b23d2c
-	ptrace.h \
Petr Machata 4b23d2c
-	signalent.h \
Petr Machata 4b23d2c
-	syscallent.h
Petr Machata 4b23d2c
+noinst_HEADERS = arch.h arch_syscallent.h ptrace.h regs.h signalent.h	\
Petr Machata 4b23d2c
+	 syscallent.h
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-MAINTAINERCLEANFILES = \
Petr Machata 4b23d2c
-	Makefile.in
Petr Machata 4b23d2c
+MAINTAINERCLEANFILES = Makefile.in
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h
Petr Machata cb74839
index 291443a..58a7fdf 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/arm/arch.h
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/arm/arch.h
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 1998,2004,2008 Juan Cespedes
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
  * This program is free software; you can redistribute it and/or
Petr Machata cb74839
@@ -18,6 +19,9 @@
Petr Machata cb74839
  * 02110-1301 USA
Petr Machata cb74839
  */
Petr Machata cb74839
 
Petr Machata cb74839
+#ifndef LTRACE_ARM_ARCH_H
Petr Machata cb74839
+#define LTRACE_ARM_ARCH_H
Petr Machata cb74839
+
Petr Machata cb74839
 #define ARCH_HAVE_ENABLE_BREAKPOINT 1
Petr Machata cb74839
 #define ARCH_HAVE_DISABLE_BREAKPOINT 1
Petr Machata cb74839
 
Petr Machata cb74839
@@ -31,7 +35,24 @@
Petr Machata 4b23d2c
 #define LT_ELFCLASS	ELFCLASS32
Petr Machata 4b23d2c
 #define LT_ELF_MACHINE	EM_ARM
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
+#define ARCH_HAVE_SW_SINGLESTEP
Petr Machata cb74839
+#define ARCH_HAVE_FETCH_ARG
Petr Machata cb74839
+#define ARCH_HAVE_FETCH_PACK
Petr Machata cb74839
+#define ARCH_HAVE_SIZEOF
Petr Machata cb74839
+#define ARCH_HAVE_ALIGNOF
Petr Machata 4b23d2c
 #define ARCH_HAVE_BREAKPOINT_DATA
Petr Machata 4b23d2c
 struct arch_breakpoint_data {
Petr Machata 4b23d2c
 	int thumb_mode;
Petr Machata cb74839
 };
Petr Machata cb74839
+
Petr Machata cb74839
+#define ARCH_HAVE_LTELF_DATA
Petr Machata cb74839
+struct arch_ltelf_data {
Petr Machata cb74839
+	/* We have this only for the hooks.  */
Petr Machata cb74839
+};
Petr Machata cb74839
+
Petr Machata cb74839
+#define ARCH_HAVE_LIBRARY_DATA
Petr Machata cb74839
+struct arch_library_data {
Petr Machata cb74839
+	unsigned int hardfp:1;
Petr Machata cb74839
+};
Petr Machata cb74839
+
Petr Machata cb74839
+#endif /* LTRACE_ARM_ARCH_H */
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c
Petr Machata 4b23d2c
index 2fb9578..fcd43a7 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/arm/breakpoint.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/arm/breakpoint.c
Petr Machata 4b23d2c
@@ -94,14 +94,11 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp)
Petr Machata 4b23d2c
 int
Petr Machata 4b23d2c
 arch_breakpoint_init(struct process *proc, struct breakpoint *sbp)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
-	/* XXX That uintptr_t cast is there temporarily until
Petr Machata 4b23d2c
-	 * arch_addr_t becomes integral type.  */
Petr Machata 4b23d2c
-	int thumb_mode = ((uintptr_t)sbp->addr) & 1;
Petr Machata 4b23d2c
-	if (thumb_mode)
Petr Machata 4b23d2c
-		sbp->addr = (void *)((uintptr_t)sbp->addr & ~1);
Petr Machata 4b23d2c
-	sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode;
Petr Machata 4b23d2c
-	/* XXX This doesn't seem like it belongs here.  */
Petr Machata 4b23d2c
-	proc->thumb_mode = 0;
Petr Machata 4b23d2c
+	/* XXX double cast  */
Petr Machata 4b23d2c
+	sbp->arch.thumb_mode = ((uintptr_t)sbp->addr) & 1;
Petr Machata 4b23d2c
+	if (sbp->arch.thumb_mode)
Petr Machata 4b23d2c
+		/* XXX double cast */
Petr Machata 4b23d2c
+		sbp->addr = (arch_addr_t)((uintptr_t)sbp->addr & ~1);
Petr Machata 4b23d2c
 	return 0;
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
 
Petr Machata cb74839
diff --git a/sysdeps/linux-gnu/arm/fetch.c b/sysdeps/linux-gnu/arm/fetch.c
Petr Machata cb74839
new file mode 100644
Petr Machata cb74839
index 0000000..0064d91
Petr Machata cb74839
--- /dev/null
Petr Machata cb74839
+++ b/sysdeps/linux-gnu/arm/fetch.c
Petr Machata cb74839
@@ -0,0 +1,529 @@
Petr Machata cb74839
+/*
Petr Machata cb74839
+ * This file is part of ltrace.
Petr Machata cb74839
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata cb74839
+ *
Petr Machata cb74839
+ * This program is free software; you can redistribute it and/or
Petr Machata cb74839
+ * modify it under the terms of the GNU General Public License as
Petr Machata cb74839
+ * published by the Free Software Foundation; either version 2 of the
Petr Machata cb74839
+ * License, or (at your option) any later version.
Petr Machata cb74839
+ *
Petr Machata cb74839
+ * This program is distributed in the hope that it will be useful, but
Petr Machata cb74839
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
Petr Machata cb74839
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Petr Machata cb74839
+ * General Public License for more details.
Petr Machata cb74839
+ *
Petr Machata cb74839
+ * You should have received a copy of the GNU General Public License
Petr Machata cb74839
+ * along with this program; if not, write to the Free Software
Petr Machata cb74839
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Petr Machata cb74839
+ * 02110-1301 USA
Petr Machata cb74839
+ */
Petr Machata cb74839
+
Petr Machata cb74839
+#include <sys/ptrace.h>
Petr Machata cb74839
+#include <asm/ptrace.h>
Petr Machata cb74839
+#include <assert.h>
Petr Machata cb74839
+#include <elf.h>
Petr Machata cb74839
+#include <libelf.h>
Petr Machata cb74839
+#include <stdint.h>
Petr Machata cb74839
+#include <stdio.h>
Petr Machata cb74839
+#include <stdlib.h>
Petr Machata cb74839
+#include <string.h>
Petr Machata cb74839
+#include <stdbool.h>
Petr Machata cb74839
+
Petr Machata cb74839
+#include "backend.h"
Petr Machata cb74839
+#include "fetch.h"
Petr Machata cb74839
+#include "library.h"
Petr Machata cb74839
+#include "ltrace-elf.h"
Petr Machata cb74839
+#include "proc.h"
Petr Machata cb74839
+#include "ptrace.h"
Petr Machata cb74839
+#include "regs.h"
Petr Machata cb74839
+#include "type.h"
Petr Machata cb74839
+#include "value.h"
Petr Machata cb74839
+
Petr Machata cb74839
+static int
Petr Machata cb74839
+get_hardfp(uint64_t abi_vfp_args)
Petr Machata cb74839
+{
Petr Machata cb74839
+	if (abi_vfp_args == 2)
Petr Machata cb74839
+		fprintf(stderr,
Petr Machata cb74839
+			"Tag_ABI_VFP_args value 2 (tool chain-specific "
Petr Machata cb74839
+			"conventions) not supported.\n");
Petr Machata cb74839
+	return abi_vfp_args == 1;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+int
Petr Machata cb74839
+arch_elf_init(struct ltelf *lte, struct library *lib)
Petr Machata cb74839
+{
Petr Machata cb74839
+	/* Nothing in this section is strictly critical.  It's not
Petr Machata cb74839
+	 * that much of a deal if we fail to guess right whether the
Petr Machata cb74839
+	 * ABI is softfp or hardfp.  */
Petr Machata cb74839
+	unsigned hardfp = 0;
Petr Machata cb74839
+
Petr Machata cb74839
+	Elf_Scn *scn;
Petr Machata cb74839
+	Elf_Data *data;
Petr Machata cb74839
+	GElf_Shdr shdr;
Petr Machata cb74839
+	if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0
Petr Machata cb74839
+	    || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) {
Petr Machata cb74839
+		fprintf(stderr,
Petr Machata cb74839
+			"Error when obtaining ARM attribute section: %s\n",
Petr Machata cb74839
+			elf_errmsg(-1));
Petr Machata cb74839
+		goto done;
Petr Machata cb74839
+
Petr Machata cb74839
+	} else if (scn != NULL && data != NULL) {
Petr Machata cb74839
+		GElf_Xword offset = 0;
Petr Machata cb74839
+		uint8_t version;
Petr Machata cb74839
+		if (elf_read_next_u8(data, &offset, &version) < 0) {
Petr Machata cb74839
+			goto done;
Petr Machata cb74839
+		} else if (version != 'A') {
Petr Machata cb74839
+			fprintf(stderr, "Unsupported ARM attribute section "
Petr Machata cb74839
+				"version %d ('%c').\n", version, version);
Petr Machata cb74839
+			goto done;
Petr Machata cb74839
+		}
Petr Machata cb74839
+
Petr Machata cb74839
+		do {
Petr Machata cb74839
+			const char signature[] = "aeabi";
Petr Machata cb74839
+			/* N.B. LEN is including the length field
Petr Machata cb74839
+			 * itself.  */
Petr Machata cb74839
+			uint32_t sec_len;
Petr Machata cb74839
+			if (elf_read_u32(data, offset, &sec_len) < 0
Petr Machata cb74839
+			    || !elf_can_read_next(data, offset, sec_len)) {
Petr Machata cb74839
+				goto done;
Petr Machata cb74839
+			}
Petr Machata cb74839
+			const GElf_Xword next_offset = offset + sec_len;
Petr Machata cb74839
+			offset += 4;
Petr Machata cb74839
+
Petr Machata cb74839
+			if (sec_len < 4 + sizeof signature
Petr Machata cb74839
+			    || strcmp(signature, data->d_buf + offset) != 0)
Petr Machata cb74839
+				goto skip;
Petr Machata cb74839
+			offset += sizeof signature;
Petr Machata cb74839
+
Petr Machata cb74839
+			const GElf_Xword offset0 = offset;
Petr Machata cb74839
+			uint64_t tag;
Petr Machata cb74839
+			uint32_t sub_len;
Petr Machata cb74839
+			if (elf_read_next_uleb128(data, &offset, &tag) < 0
Petr Machata cb74839
+			    || elf_read_next_u32(data, &offset, &sub_len) < 0
Petr Machata cb74839
+			    || !elf_can_read_next(data, offset0, sub_len))
Petr Machata cb74839
+				goto done;
Petr Machata cb74839
+
Petr Machata cb74839
+			if (tag != 1)
Petr Machata cb74839
+				/* IHI0045D_ABI_addenda: "section and
Petr Machata cb74839
+				 * symbol attributes are deprecated
Petr Machata cb74839
+				 * [...] consumers are permitted to
Petr Machata cb74839
+				 * ignore them."  */
Petr Machata cb74839
+				goto skip;
Petr Machata cb74839
+
Petr Machata cb74839
+			while (offset < offset0 + sub_len) {
Petr Machata cb74839
+				if (elf_read_next_uleb128(data,
Petr Machata cb74839
+							  &offset, &tag) < 0)
Petr Machata cb74839
+					goto done;
Petr Machata cb74839
+
Petr Machata cb74839
+				switch (tag) {
Petr Machata cb74839
+					uint64_t v;
Petr Machata cb74839
+				case 6: /* Tag_CPU_arch */
Petr Machata cb74839
+				case 7: /* Tag_CPU_arch_profile */
Petr Machata cb74839
+				case 8: /* Tag_ARM_ISA_use */
Petr Machata cb74839
+				case 9: /* Tag_THUMB_ISA_use */
Petr Machata cb74839
+				case 10: /* Tag_FP_arch */
Petr Machata cb74839
+				case 11: /* Tag_WMMX_arch */
Petr Machata cb74839
+				case 12: /* Tag_Advanced_SIMD_arch */
Petr Machata cb74839
+				case 13: /* Tag_PCS_config */
Petr Machata cb74839
+				case 14: /* Tag_ABI_PCS_R9_use */
Petr Machata cb74839
+				case 15: /* Tag_ABI_PCS_RW_data */
Petr Machata cb74839
+				case 16: /* Tag_ABI_PCS_RO_data */
Petr Machata cb74839
+				case 17: /* Tag_ABI_PCS_GOT_use */
Petr Machata cb74839
+				case 18: /* Tag_ABI_PCS_wchar_t */
Petr Machata cb74839
+				case 19: /* Tag_ABI_FP_rounding */
Petr Machata cb74839
+				case 20: /* Tag_ABI_FP_denormal */
Petr Machata cb74839
+				case 21: /* Tag_ABI_FP_exceptions */
Petr Machata cb74839
+				case 22: /* Tag_ABI_FP_user_exceptions */
Petr Machata cb74839
+				case 23: /* Tag_ABI_FP_number_model */
Petr Machata cb74839
+				case 24: /* Tag_ABI_align_needed */
Petr Machata cb74839
+				case 25: /* Tag_ABI_align_preserved */
Petr Machata cb74839
+				case 26: /* Tag_ABI_enum_size */
Petr Machata cb74839
+				case 27: /* Tag_ABI_HardFP_use */
Petr Machata cb74839
+				case 28: /* Tag_ABI_VFP_args */
Petr Machata cb74839
+				case 29: /* Tag_ABI_WMMX_args */
Petr Machata cb74839
+				case 30: /* Tag_ABI_optimization_goals */
Petr Machata cb74839
+				case 31: /* Tag_ABI_FP_optimization_goals */
Petr Machata cb74839
+				case 32: /* Tag_compatibility */
Petr Machata cb74839
+				case 34: /* Tag_CPU_unaligned_access */
Petr Machata cb74839
+				case 36: /* Tag_FP_HP_extension */
Petr Machata cb74839
+				case 38: /* Tag_ABI_FP_16bit_format */
Petr Machata cb74839
+				case 42: /* Tag_MPextension_use */
Petr Machata cb74839
+				case 70: /* Tag_MPextension_use as well */
Petr Machata cb74839
+				case 44: /* Tag_DIV_use */
Petr Machata cb74839
+				case 64: /* Tag_nodefaults */
Petr Machata cb74839
+				case 66: /* Tag_T2EE_use */
Petr Machata cb74839
+				case 68: /* Tag_Virtualization_use */
Petr Machata cb74839
+				uleb128:
Petr Machata cb74839
+					if (elf_read_next_uleb128
Petr Machata cb74839
+						(data, &offset, &v) < 0)
Petr Machata cb74839
+						goto done;
Petr Machata cb74839
+					if (tag == 28)
Petr Machata cb74839
+						hardfp = get_hardfp(v);
Petr Machata cb74839
+					if (tag != 32)
Petr Machata cb74839
+						continue;
Petr Machata cb74839
+
Petr Machata cb74839
+					/* Tag 32 has two arguments,
Petr Machata cb74839
+					 * fall through.  */
Petr Machata cb74839
+
Petr Machata cb74839
+				case 4:	/* Tag_CPU_raw_name */
Petr Machata cb74839
+				case 5:	/* Tag_CPU_name */
Petr Machata cb74839
+				case 65: /* Tag_also_compatible_with */
Petr Machata cb74839
+				case 67: /* Tag_conformance */
Petr Machata cb74839
+				ntbs:
Petr Machata cb74839
+					offset += strlen(data->d_buf
Petr Machata cb74839
+							 + offset) + 1;
Petr Machata cb74839
+					continue;
Petr Machata cb74839
+				}
Petr Machata cb74839
+
Petr Machata cb74839
+				/* Handle unknown tags in a generic
Petr Machata cb74839
+				 * manner, if possible.  */
Petr Machata cb74839
+				if (tag <= 32) {
Petr Machata cb74839
+					fprintf(stderr,
Petr Machata cb74839
+						"Unknown tag %lld "
Petr Machata cb74839
+						"at offset %#llx "
Petr Machata cb74839
+						"of ARM attribute section.",
Petr Machata cb74839
+						tag, offset);
Petr Machata cb74839
+					goto skip;
Petr Machata cb74839
+				} else if (tag % 2 == 0) {
Petr Machata cb74839
+					goto uleb128;
Petr Machata cb74839
+				} else {
Petr Machata cb74839
+					goto ntbs;
Petr Machata cb74839
+				}
Petr Machata cb74839
+			}
Petr Machata cb74839
+
Petr Machata cb74839
+		skip:
Petr Machata cb74839
+			offset = next_offset;
Petr Machata cb74839
+
Petr Machata cb74839
+		} while (elf_can_read_next(data, offset, 1));
Petr Machata cb74839
+
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+done:
Petr Machata cb74839
+	lib->arch.hardfp = hardfp;
Petr Machata cb74839
+	return 0;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void
Petr Machata cb74839
+arch_elf_destroy(struct ltelf *lte)
Petr Machata cb74839
+{
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void
Petr Machata cb74839
+arch_library_init(struct library *lib)
Petr Machata cb74839
+{
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void
Petr Machata cb74839
+arch_library_destroy(struct library *lib)
Petr Machata cb74839
+{
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void
Petr Machata cb74839
+arch_library_clone(struct library *retp, struct library *lib)
Petr Machata cb74839
+{
Petr Machata cb74839
+	retp->arch = lib->arch;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+enum {
Petr Machata cb74839
+	/* How many (double) VFP registers the AAPCS uses for
Petr Machata cb74839
+	 * parameter passing.  */
Petr Machata cb74839
+	NUM_VFP_REGS = 8,
Petr Machata cb74839
+};
Petr Machata cb74839
+
Petr Machata cb74839
+struct fetch_context {
Petr Machata cb74839
+	struct pt_regs regs;
Petr Machata cb74839
+
Petr Machata cb74839
+	struct {
Petr Machata cb74839
+		union {
Petr Machata cb74839
+			double d[32];
Petr Machata cb74839
+			float s[64];
Petr Machata cb74839
+		};
Petr Machata cb74839
+		uint32_t fpscr;
Petr Machata cb74839
+	} fpregs;
Petr Machata cb74839
+
Petr Machata cb74839
+	/* VFP register allocation.  ALLOC.S tracks whether the
Petr Machata cb74839
+	 * corresponding FPREGS.S register is taken, ALLOC.D the same
Petr Machata cb74839
+	 * for FPREGS.D.  We only track 8 (16) registers, because
Petr Machata cb74839
+	 * that's what the ABI uses for parameter passing.  */
Petr Machata cb74839
+	union {
Petr Machata cb74839
+		int16_t d[NUM_VFP_REGS];
Petr Machata cb74839
+		int8_t s[NUM_VFP_REGS * 2];
Petr Machata cb74839
+	} alloc;
Petr Machata cb74839
+
Petr Machata cb74839
+	unsigned ncrn;
Petr Machata cb74839
+	arch_addr_t sp;
Petr Machata cb74839
+	arch_addr_t nsaa;
Petr Machata cb74839
+	arch_addr_t ret_struct;
Petr Machata cb74839
+
Petr Machata cb74839
+	bool hardfp:1;
Petr Machata cb74839
+	bool in_varargs:1;
Petr Machata cb74839
+};
Petr Machata cb74839
+
Petr Machata cb74839
+static int
Petr Machata cb74839
+fetch_register_banks(struct process *proc, struct fetch_context *context)
Petr Machata cb74839
+{
Petr Machata cb74839
+	if (ptrace(PTRACE_GETREGS, proc->pid, NULL, &context->regs) == -1)
Petr Machata cb74839
+		return -1;
Petr Machata cb74839
+
Petr Machata cb74839
+	if (context->hardfp
Petr Machata cb74839
+	    && ptrace(PTRACE_GETVFPREGS, proc->pid,
Petr Machata cb74839
+		      NULL, &context->fpregs) == -1)
Petr Machata cb74839
+		return -1;
Petr Machata cb74839
+
Petr Machata cb74839
+	context->ncrn = 0;
Petr Machata cb74839
+	context->nsaa = context->sp = get_stack_pointer(proc);
Petr Machata cb74839
+	memset(&context->alloc, 0, sizeof(context->alloc));
Petr Machata cb74839
+
Petr Machata cb74839
+	return 0;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+struct fetch_context *
Petr Machata cb74839
+arch_fetch_arg_init(enum tof type, struct process *proc,
Petr Machata cb74839
+		    struct arg_type_info *ret_info)
Petr Machata cb74839
+{
Petr Machata cb74839
+	struct fetch_context *context = malloc(sizeof(*context));
Petr Machata cb74839
+
Petr Machata cb74839
+	{
Petr Machata cb74839
+		struct process *mainp = proc;
Petr Machata cb74839
+		while (mainp->libraries == NULL && mainp->parent != NULL)
Petr Machata cb74839
+			mainp = mainp->parent;
Petr Machata cb74839
+		context->hardfp = mainp->libraries->arch.hardfp;
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	if (context == NULL
Petr Machata cb74839
+	    || fetch_register_banks(proc, context) < 0) {
Petr Machata cb74839
+		free(context);
Petr Machata cb74839
+		return NULL;
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	if (ret_info->type == ARGTYPE_STRUCT
Petr Machata cb74839
+	    || ret_info->type == ARGTYPE_ARRAY) {
Petr Machata cb74839
+		size_t sz = type_sizeof(proc, ret_info);
Petr Machata cb74839
+		assert(sz != (size_t)-1);
Petr Machata cb74839
+		if (sz > 4) {
Petr Machata cb74839
+			/* XXX double cast */
Petr Machata cb74839
+			context->ret_struct
Petr Machata cb74839
+				= (arch_addr_t)context->regs.uregs[0];
Petr Machata cb74839
+			context->ncrn++;
Petr Machata cb74839
+		}
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	return context;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+struct fetch_context *
Petr Machata cb74839
+arch_fetch_arg_clone(struct process *proc,
Petr Machata cb74839
+		     struct fetch_context *context)
Petr Machata cb74839
+{
Petr Machata cb74839
+	struct fetch_context *clone = malloc(sizeof(*context));
Petr Machata cb74839
+	if (clone == NULL)
Petr Machata cb74839
+		return NULL;
Petr Machata cb74839
+	*clone = *context;
Petr Machata cb74839
+	return clone;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+/* 0 is success, 1 is failure, negative value is an error.  */
Petr Machata cb74839
+static int
Petr Machata cb74839
+pass_in_vfp(struct fetch_context *ctx, struct process *proc,
Petr Machata cb74839
+	    enum arg_type type, size_t count, struct value *valuep)
Petr Machata cb74839
+{
Petr Machata cb74839
+	assert(type == ARGTYPE_FLOAT || type == ARGTYPE_DOUBLE);
Petr Machata cb74839
+	unsigned max = type == ARGTYPE_DOUBLE ? NUM_VFP_REGS : 2 * NUM_VFP_REGS;
Petr Machata cb74839
+	if (count > max)
Petr Machata cb74839
+		return 1;
Petr Machata cb74839
+
Petr Machata cb74839
+	size_t i;
Petr Machata cb74839
+	size_t j;
Petr Machata cb74839
+	for (i = 0; i < max; ++i) {
Petr Machata cb74839
+		for (j = i; j < i + count; ++j)
Petr Machata cb74839
+			if ((type == ARGTYPE_DOUBLE && ctx->alloc.d[j] != 0)
Petr Machata cb74839
+			    || (type == ARGTYPE_FLOAT && ctx->alloc.s[j] != 0))
Petr Machata cb74839
+				goto next;
Petr Machata cb74839
+
Petr Machata cb74839
+		/* Found COUNT consecutive unallocated registers at I.  */
Petr Machata cb74839
+		const size_t sz = (type == ARGTYPE_FLOAT ? 4 : 8) * count;
Petr Machata cb74839
+		unsigned char *data = value_reserve(valuep, sz);
Petr Machata cb74839
+		if (data == NULL)
Petr Machata cb74839
+			return -1;
Petr Machata cb74839
+
Petr Machata cb74839
+		for (j = i; j < i + count; ++j)
Petr Machata cb74839
+			if (type == ARGTYPE_DOUBLE)
Petr Machata cb74839
+				ctx->alloc.d[j] = -1;
Petr Machata cb74839
+			else
Petr Machata cb74839
+				ctx->alloc.s[j] = -1;
Petr Machata cb74839
+
Petr Machata cb74839
+		if (type == ARGTYPE_DOUBLE)
Petr Machata cb74839
+			memcpy(data, ctx->fpregs.d + i, sz);
Petr Machata cb74839
+		else
Petr Machata cb74839
+			memcpy(data, ctx->fpregs.s + i, sz);
Petr Machata cb74839
+
Petr Machata cb74839
+		return 0;
Petr Machata cb74839
+
Petr Machata cb74839
+	next:
Petr Machata cb74839
+		continue;
Petr Machata cb74839
+	}
Petr Machata cb74839
+	return 1;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+/* 0 is success, 1 is failure, negative value is an error.  */
Petr Machata cb74839
+static int
Petr Machata cb74839
+consider_vfp(struct fetch_context *ctx, struct process *proc,
Petr Machata cb74839
+	     struct arg_type_info *info, struct value *valuep)
Petr Machata cb74839
+{
Petr Machata cb74839
+	struct arg_type_info *float_info = NULL;
Petr Machata cb74839
+	size_t hfa_size = 1;
Petr Machata cb74839
+	if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE)
Petr Machata cb74839
+		float_info = info;
Petr Machata cb74839
+	else
Petr Machata cb74839
+		float_info = type_get_hfa_type(info, &hfa_size);
Petr Machata cb74839
+
Petr Machata cb74839
+	if (float_info != NULL && hfa_size <= 4)
Petr Machata cb74839
+		return pass_in_vfp(ctx, proc, float_info->type,
Petr Machata cb74839
+				   hfa_size, valuep);
Petr Machata cb74839
+	return 1;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+int
Petr Machata cb74839
+arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
Petr Machata cb74839
+		    struct process *proc,
Petr Machata cb74839
+		    struct arg_type_info *info, struct value *valuep)
Petr Machata cb74839
+{
Petr Machata cb74839
+	const size_t sz = type_sizeof(proc, info);
Petr Machata cb74839
+	assert(sz != (size_t)-1);
Petr Machata cb74839
+
Petr Machata cb74839
+	if (ctx->hardfp && !ctx->in_varargs) {
Petr Machata cb74839
+		int rc;
Petr Machata cb74839
+		if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1)
Petr Machata cb74839
+			return rc;
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	/* IHI0042E_aapcs: If the argument requires double-word
Petr Machata cb74839
+	 * alignment (8-byte), the NCRN is rounded up to the next even
Petr Machata cb74839
+	 * register number.  */
Petr Machata cb74839
+	const size_t al = type_alignof(proc, info);
Petr Machata cb74839
+	assert(al != (size_t)-1);
Petr Machata cb74839
+	if (al == 8)
Petr Machata cb74839
+		ctx->ncrn = ((ctx->ncrn + 1) / 2) * 2;
Petr Machata cb74839
+
Petr Machata cb74839
+	/* If the size in words of the argument is not more than r4
Petr Machata cb74839
+	 * minus NCRN, the argument is copied into core registers,
Petr Machata cb74839
+	 * starting at the NCRN.  */
Petr Machata cb74839
+	/* If the NCRN is less than r4 and the NSAA is equal to the
Petr Machata cb74839
+	 * SP, the argument is split between core registers and the
Petr Machata cb74839
+	 * stack.  */
Petr Machata cb74839
+
Petr Machata cb74839
+	const size_t words = (sz + 3) / 4;
Petr Machata cb74839
+	if (ctx->ncrn < 4 && ctx->nsaa == ctx->sp) {
Petr Machata cb74839
+		unsigned char *data = value_reserve(valuep, words * 4);
Petr Machata cb74839
+		if (data == NULL)
Petr Machata cb74839
+			return -1;
Petr Machata cb74839
+		size_t i;
Petr Machata cb74839
+		for (i = 0; i < words && ctx->ncrn < 4; ++i) {
Petr Machata cb74839
+			memcpy(data, &ctx->regs.uregs[ctx->ncrn++], 4);
Petr Machata cb74839
+			data += 4;
Petr Machata cb74839
+		}
Petr Machata cb74839
+		const size_t rest = (words - i) * 4;
Petr Machata cb74839
+		if (rest > 0) {
Petr Machata cb74839
+			umovebytes(proc, ctx->nsaa, data, rest);
Petr Machata cb74839
+			ctx->nsaa += rest;
Petr Machata cb74839
+		}
Petr Machata cb74839
+		return 0;
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	assert(ctx->ncrn == 4);
Petr Machata cb74839
+
Petr Machata cb74839
+	/* If the argument required double-word alignment (8-byte),
Petr Machata cb74839
+	 * then the NSAA is rounded up to the next double-word
Petr Machata cb74839
+	 * address.  */
Petr Machata cb74839
+	if (al == 8)
Petr Machata cb74839
+		/* XXX double cast.  */
Petr Machata cb74839
+		ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 7) / 8) * 8);
Petr Machata cb74839
+	else
Petr Machata cb74839
+		ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 3) / 4) * 4);
Petr Machata cb74839
+
Petr Machata cb74839
+	value_in_inferior(valuep, ctx->nsaa);
Petr Machata cb74839
+	ctx->nsaa += sz;
Petr Machata cb74839
+
Petr Machata cb74839
+	return 0;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+int
Petr Machata cb74839
+arch_fetch_retval(struct fetch_context *ctx, enum tof type,
Petr Machata cb74839
+		  struct process *proc, struct arg_type_info *info,
Petr Machata cb74839
+		  struct value *valuep)
Petr Machata cb74839
+{
Petr Machata cb74839
+	if (fetch_register_banks(proc, ctx) < 0)
Petr Machata cb74839
+		return -1;
Petr Machata cb74839
+
Petr Machata cb74839
+	if (ctx->hardfp && !ctx->in_varargs) {
Petr Machata cb74839
+		int rc;
Petr Machata cb74839
+		if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1)
Petr Machata cb74839
+			return rc;
Petr Machata cb74839
+	}
Petr Machata cb74839
+
Petr Machata cb74839
+	size_t sz = type_sizeof(proc, info);
Petr Machata cb74839
+	assert(sz != (size_t)-1);
Petr Machata cb74839
+
Petr Machata cb74839
+	switch (info->type) {
Petr Machata cb74839
+		unsigned char *data;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_VOID:
Petr Machata cb74839
+		return 0;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_FLOAT:
Petr Machata cb74839
+	case ARGTYPE_DOUBLE:
Petr Machata cb74839
+		if (ctx->hardfp && !ctx->in_varargs) {
Petr Machata cb74839
+			unsigned char *data = value_reserve(valuep, sz);
Petr Machata cb74839
+			if (data == NULL)
Petr Machata cb74839
+				return -1;
Petr Machata cb74839
+			memmove(data, &ctx->fpregs, sz);
Petr Machata cb74839
+			return 0;
Petr Machata cb74839
+		}
Petr Machata cb74839
+		goto pass_in_registers;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_ARRAY:
Petr Machata cb74839
+	case ARGTYPE_STRUCT:
Petr Machata cb74839
+		if (sz > 4) {
Petr Machata cb74839
+			value_in_inferior(valuep, ctx->ret_struct);
Petr Machata cb74839
+			return 0;
Petr Machata cb74839
+		}
Petr Machata cb74839
+		/* Fall through.  */
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_CHAR:
Petr Machata cb74839
+	case ARGTYPE_SHORT:
Petr Machata cb74839
+	case ARGTYPE_USHORT:
Petr Machata cb74839
+	case ARGTYPE_INT:
Petr Machata cb74839
+	case ARGTYPE_UINT:
Petr Machata cb74839
+	case ARGTYPE_LONG:
Petr Machata cb74839
+	case ARGTYPE_ULONG:
Petr Machata cb74839
+	case ARGTYPE_POINTER:
Petr Machata cb74839
+	pass_in_registers:
Petr Machata cb74839
+		if ((data = value_reserve(valuep, sz)) == NULL)
Petr Machata cb74839
+			return -1;
Petr Machata cb74839
+		memmove(data, ctx->regs.uregs, sz);
Petr Machata cb74839
+		return 0;
Petr Machata cb74839
+	}
Petr Machata cb74839
+	assert(info->type != info->type);
Petr Machata cb74839
+	abort();
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void
Petr Machata cb74839
+arch_fetch_arg_done(struct fetch_context *context)
Petr Machata cb74839
+{
Petr Machata cb74839
+	free(context);
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+int
Petr Machata cb74839
+arch_fetch_param_pack_start(struct fetch_context *context,
Petr Machata cb74839
+			    enum param_pack_flavor ppflavor)
Petr Machata cb74839
+{
Petr Machata cb74839
+	if (ppflavor == PARAM_PACK_VARARGS)
Petr Machata cb74839
+		context->in_varargs = true;
Petr Machata cb74839
+	return 0;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void
Petr Machata cb74839
+arch_fetch_param_pack_end(struct fetch_context *context)
Petr Machata cb74839
+{
Petr Machata cb74839
+	context->in_varargs = false;
Petr Machata cb74839
+}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c
Petr Machata cb74839
index 377df62..e9e825e 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/arm/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/arm/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2009 Juan Cespedes
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
@@ -24,9 +25,11 @@
Petr Machata 4b23d2c
 #include <sys/types.h>
Petr Machata 4b23d2c
 #include <sys/ptrace.h>
Petr Machata 4b23d2c
 #include <asm/ptrace.h>
Petr Machata 4b23d2c
+#include <errno.h>
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 #include "proc.h"
Petr Machata 4b23d2c
 #include "common.h"
Petr Machata 4b23d2c
+#include "regs.h"
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
Petr Machata 4b23d2c
 # define PTRACE_PEEKUSER PTRACE_PEEKUSR
Petr Machata cb74839
@@ -36,50 +39,119 @@
Petr Machata cb74839
 # define PTRACE_POKEUSER PTRACE_POKEUSR
Petr Machata 4b23d2c
 #endif
Petr Machata 4b23d2c
 
Petr Machata cb74839
-#define off_pc ((void *)60)
Petr Machata 4b23d2c
-#define off_lr ((void *)56)
Petr Machata cb74839
-#define off_sp ((void *)52)
Petr Machata 4b23d2c
+int
Petr Machata 4b23d2c
+arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp)
Petr Machata 4b23d2c
+{
Petr Machata 4b23d2c
+	errno = 0;
Petr Machata 4b23d2c
+	long l = ptrace(PTRACE_PEEKUSER, proc->pid, (void *)(reg * 4L), 0);
Petr Machata 4b23d2c
+	if (l == -1 && errno != 0)
Petr Machata 4b23d2c
+		return -1;
Petr Machata 4b23d2c
+	*lp = (uint32_t)l;
Petr Machata 4b23d2c
+	return 0;
Petr Machata 4b23d2c
+}
Petr Machata cb74839
 
Petr Machata cb74839
-void *
Petr Machata cb74839
-get_instruction_pointer(struct process *proc)
Petr Machata cb74839
+int
Petr Machata cb74839
+arm_set_register(struct process *proc, enum arm_register reg, uint32_t lp)
Petr Machata cb74839
 {
Petr Machata cb74839
-	return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
Petr Machata cb74839
+	return ptrace(PTRACE_PEEKUSER, proc->pid,
Petr Machata cb74839
+		      (void *)(reg * 4L), (void *)lp);
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
-void
Petr Machata cb74839
-set_instruction_pointer(struct process *proc, void *addr)
Petr Machata 4b23d2c
+int
Petr Machata 4b23d2c
+arm_get_register_offpc(struct process *proc, enum arm_register reg,
Petr Machata 4b23d2c
+		       uint32_t *lp)
Petr Machata cb74839
 {
Petr Machata cb74839
-	ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr);
Petr Machata 4b23d2c
+	if (arm_get_register(proc, reg, lp) < 0)
Petr Machata 4b23d2c
+		return -1;
Petr Machata 4b23d2c
+	if (reg == ARM_REG_PC)
Petr Machata 4b23d2c
+		*lp += 8;
Petr Machata 4b23d2c
+	return 0;
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
-void *
Petr Machata cb74839
-get_stack_pointer(struct process *proc)
Petr Machata 4b23d2c
+int
Petr Machata 4b23d2c
+arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
Petr Machata 4b23d2c
+			 arch_addr_t pc_val, uint32_t *lp)
Petr Machata cb74839
 {
Petr Machata cb74839
-	return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0);
Petr Machata 4b23d2c
+	enum arm_register rm = BITS(inst, 0, 3);
Petr Machata 4b23d2c
+	unsigned long shifttype = BITS(inst, 5, 6);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	uint32_t shift;
Petr Machata 4b23d2c
+	if (BIT(inst, 4)) {
Petr Machata 4b23d2c
+		if (arm_get_register_offpc(proc, BITS(inst, 8, 11), &shift) < 0)
Petr Machata 4b23d2c
+			return -1;
Petr Machata 4b23d2c
+		shift &= 0xff;
Petr Machata 4b23d2c
+	} else {
Petr Machata 4b23d2c
+		shift = BITS(inst, 7, 11);
Petr Machata 4b23d2c
+	}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	uint32_t res;
Petr Machata 4b23d2c
+	if (rm == ARM_REG_PC)
Petr Machata 4b23d2c
+		/* xxx double cast */
Petr Machata 4b23d2c
+		res = (uintptr_t)pc_val + (BIT(inst, 4) ? 12 : 8);
Petr Machata 4b23d2c
+	else if (arm_get_register(proc, rm, &res) < 0)
Petr Machata 4b23d2c
+		return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	switch (shifttype) {
Petr Machata 4b23d2c
+	case 0:			/* LSL */
Petr Machata 4b23d2c
+		res = shift >= 32 ? 0 : res << shift;
Petr Machata 4b23d2c
+		break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	case 1:			/* LSR */
Petr Machata 4b23d2c
+		res = shift >= 32 ? 0 : res >> shift;
Petr Machata 4b23d2c
+		break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	case 2:			/* ASR */
Petr Machata 4b23d2c
+		if (shift >= 32)
Petr Machata 4b23d2c
+			shift = 31;
Petr Machata 4b23d2c
+		res = ((res & 0x80000000L)
Petr Machata 4b23d2c
+		       ? ~((~res) >> shift) : res >> shift);
Petr Machata 4b23d2c
+		break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	case 3:			/* ROR/RRX */
Petr Machata 4b23d2c
+		shift &= 31;
Petr Machata 4b23d2c
+		if (shift == 0)
Petr Machata 4b23d2c
+			res = (res >> 1) | (carry ? 0x80000000L : 0);
Petr Machata 4b23d2c
+		else
Petr Machata 4b23d2c
+			res = (res >> shift) | (res << (32 - shift));
Petr Machata 4b23d2c
+		break;
Petr Machata 4b23d2c
+	}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	*lp = res & 0xffffffff;
Petr Machata 4b23d2c
+	return 0;
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-/* really, this is given the *stack_pointer expecting
Petr Machata 4b23d2c
- * a CISC architecture; in our case, we don't need that */
Petr Machata 4b23d2c
-void *
Petr Machata 4b23d2c
-get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata cb74839
+static arch_addr_t
Petr Machata cb74839
+get_register_nocheck(struct process *proc, enum arm_register r)
Petr Machata cb74839
 {
Petr Machata 4b23d2c
-	long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0);
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-	/* Remember & unset the thumb mode bit.  XXX This is really a
Petr Machata 4b23d2c
-	 * bit of a hack, as we assume that the following
Petr Machata 4b23d2c
-	 * insert_breakpoint call will be related to this address.
Petr Machata 4b23d2c
-	 * This interface should really be get_return_breakpoint, or
Petr Machata 4b23d2c
-	 * maybe install_return_breakpoint.  */
Petr Machata 4b23d2c
-	proc->thumb_mode = addr & 1;
Petr Machata 4b23d2c
-	if (proc->thumb_mode)
Petr Machata 4b23d2c
-		addr &= ~1;
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-	return (void *)addr;
Petr Machata cb74839
+	uint32_t reg;
Petr Machata cb74839
+	if (arm_get_register(proc, r, &reg) < 0)
Petr Machata cb74839
+		/* XXX double cast. */
Petr Machata cb74839
+		return (arch_addr_t)-1;
Petr Machata cb74839
+	/* XXX double cast.  */
Petr Machata cb74839
+	return (arch_addr_t)(uintptr_t)reg;
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+arch_addr_t
Petr Machata cb74839
+get_instruction_pointer(struct process *proc)
Petr Machata cb74839
+{
Petr Machata cb74839
+	return get_register_nocheck(proc, ARM_REG_PC);
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
 void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata cb74839
+set_instruction_pointer(struct process *proc, arch_addr_t addr)
Petr Machata cb74839
+{
Petr Machata cb74839
+	/* XXX double cast.  */
Petr Machata cb74839
+	arm_set_register(proc, ARM_REG_PC, (uint32_t)addr);
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+void *
Petr Machata cb74839
+get_stack_pointer(struct process *proc)
Petr Machata cb74839
+{
Petr Machata cb74839
+	return get_register_nocheck(proc, ARM_REG_SP);
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata 4b23d2c
+arch_addr_t
Petr Machata 4b23d2c
+get_return_addr(struct process *proc, arch_addr_t stack_pointer)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
-	long iaddr = (int)addr | proc->thumb_mode;
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKEUSER, proc->pid, off_lr, (void *)iaddr);
Petr Machata cb74839
+	return get_register_nocheck(proc, ARM_REG_LR);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/arm/regs.h b/sysdeps/linux-gnu/arm/regs.h
Petr Machata 4b23d2c
new file mode 100644
Petr Machata cb74839
index 0000000..f9a5a86
Petr Machata 4b23d2c
--- /dev/null
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/arm/regs.h
Petr Machata cb74839
@@ -0,0 +1,47 @@
Petr Machata 4b23d2c
+/*
Petr Machata 4b23d2c
+ * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
+ * modify it under the terms of the GNU General Public License as
Petr Machata 4b23d2c
+ * published by the Free Software Foundation; either version 2 of the
Petr Machata 4b23d2c
+ * License, or (at your option) any later version.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * This program is distributed in the hope that it will be useful, but
Petr Machata 4b23d2c
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
Petr Machata 4b23d2c
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Petr Machata 4b23d2c
+ * General Public License for more details.
Petr Machata 4b23d2c
+ *
Petr Machata 4b23d2c
+ * You should have received a copy of the GNU General Public License
Petr Machata 4b23d2c
+ * along with this program; if not, write to the Free Software
Petr Machata 4b23d2c
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Petr Machata 4b23d2c
+ * 02110-1301 USA
Petr Machata 4b23d2c
+ */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+#define SUBMASK(x) ((1L << ((x) + 1)) - 1)
Petr Machata 4b23d2c
+#define BIT(obj,st) (((obj) >> (st)) & 1)
Petr Machata 4b23d2c
+#define BITS(obj,st,fn) (((obj) >> (st)) & SUBMASK((fn) - (st)))
Petr Machata 4b23d2c
+#define SBITS(obj,st,fn) \
Petr Machata 4b23d2c
+	((long) (BITS(obj,st,fn) | ((long) BIT(obj,fn) * ~ SUBMASK(fn - st))))
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+enum arm_register {
Petr Machata cb74839
+	ARM_REG_R7 = 7,
Petr Machata cb74839
+	ARM_REG_IP = 12,
Petr Machata 4b23d2c
+	ARM_REG_SP = 13,
Petr Machata 4b23d2c
+	ARM_REG_LR = 14,
Petr Machata 4b23d2c
+	ARM_REG_PC = 15,
Petr Machata 4b23d2c
+	ARM_REG_CPSR = 16,
Petr Machata 4b23d2c
+};
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+/* Write value of register REG to *LP.  Return 0 on success or a
Petr Machata 4b23d2c
+ * negative value on failure.  */
Petr Machata 4b23d2c
+int arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+/* Same as above, but if REG==ARM_REG_PC, it returns the value +8.  */
Petr Machata 4b23d2c
+int arm_get_register_offpc(struct process *proc, enum arm_register reg,
Petr Machata 4b23d2c
+			   uint32_t *lp);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+/* Same as arm_get_register, but shift is performed depending on
Petr Machata 4b23d2c
+ * instruction INST.  */
Petr Machata 4b23d2c
+int arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
Petr Machata 4b23d2c
+			     arch_addr_t pc, uint32_t *lp);
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
Petr Machata cb74839
index fbbf676..5e51e91 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/arm/trace.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/arm/trace.c
Petr Machata 4b23d2c
@@ -1,6 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 1998,2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Ian Wienand
Petr Machata 4b23d2c
  *
Petr Machata cb74839
@@ -29,10 +29,13 @@
Petr Machata 4b23d2c
 #include <sys/ptrace.h>
Petr Machata 4b23d2c
 #include <asm/ptrace.h>
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-#include "proc.h"
Petr Machata 4b23d2c
+#include "bits.h"
Petr Machata 4b23d2c
 #include "common.h"
Petr Machata 4b23d2c
+#include "proc.h"
Petr Machata 4b23d2c
 #include "output.h"
Petr Machata 4b23d2c
 #include "ptrace.h"
Petr Machata 4b23d2c
+#include "regs.h"
Petr Machata cb74839
+#include "type.h"
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
Petr Machata 4b23d2c
 # define PTRACE_PEEKUSER PTRACE_PEEKUSR
Petr Machata cb74839
@@ -42,11 +45,6 @@
Petr Machata cb74839
 # define PTRACE_POKEUSER PTRACE_POKEUSR
Petr Machata cb74839
 #endif
Petr Machata 4b23d2c
 
Petr Machata cb74839
-#define off_r0 ((void *)0)
Petr Machata cb74839
-#define off_r7 ((void *)28)
Petr Machata cb74839
-#define off_ip ((void *)48)
Petr Machata cb74839
-#define off_pc ((void *)60)
Petr Machata cb74839
-
Petr Machata 4b23d2c
 void
Petr Machata 4b23d2c
 get_arch_dep(struct process *proc)
Petr Machata cb74839
 {
Petr Machata cb74839
@@ -68,18 +66,24 @@ syscall_p(struct process *proc, int status, int *sysnum)
Petr Machata cb74839
 {
Petr Machata cb74839
 	if (WIFSTOPPED(status)
Petr Machata cb74839
 	    && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
Petr Machata cb74839
-		/* get the user's pc (plus 8) */
Petr Machata cb74839
-		unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
Petr Machata cb74839
+		uint32_t pc, ip;
Petr Machata cb74839
+		if (arm_get_register(proc, ARM_REG_PC, &pc) < 0
Petr Machata cb74839
+		    || arm_get_register(proc, ARM_REG_IP, &ip) < 0)
Petr Machata cb74839
+			return -1;
Petr Machata cb74839
+
Petr Machata cb74839
 		pc = pc - 4;
Petr Machata cb74839
+
Petr Machata cb74839
 		/* fetch the SWI instruction */
Petr Machata cb74839
 		unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid,
Petr Machata cb74839
 				       (void *)pc, 0);
Petr Machata cb74839
-		int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0);
Petr Machata 4b23d2c
 
Petr Machata cb74839
 		if (insn == 0xef000000 || insn == 0x0f000000
Petr Machata cb74839
 		    || (insn & 0xffff0000) == 0xdf000000) {
Petr Machata cb74839
 			/* EABI syscall */
Petr Machata cb74839
-			*sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0);
Petr Machata cb74839
+			uint32_t r7;
Petr Machata cb74839
+			if (arm_get_register(proc, ARM_REG_R7, &r7) < 0)
Petr Machata cb74839
+				return -1;
Petr Machata cb74839
+			*sysnum = r7;
Petr Machata cb74839
 		} else if ((insn & 0xfff00000) == 0xef900000) {
Petr Machata cb74839
 			/* old ABI syscall */
Petr Machata cb74839
 			*sysnum = insn & 0xfffff;
Petr Machata cb74839
@@ -105,47 +109,605 @@ syscall_p(struct process *proc, int status, int *sysnum)
Petr Machata 4b23d2c
 	return 0;
Petr Machata 4b23d2c
 }
Petr Machata cb74839
 
Petr Machata cb74839
-long
Petr Machata cb74839
-gimme_arg(enum tof type, struct process *proc, int arg_num,
Petr Machata cb74839
-	  struct arg_type_info *info)
Petr Machata 4b23d2c
+static arch_addr_t
Petr Machata 4b23d2c
+arm_branch_dest(const arch_addr_t pc, const uint32_t insn)
Petr Machata cb74839
 {
Petr Machata cb74839
-	proc_archdep *a = (proc_archdep *) proc->arch_ptr;
Petr Machata 4b23d2c
+	/* Bits 0-23 are signed immediate value.  */
Petr Machata 4b23d2c
+	return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8;
Petr Machata 4b23d2c
+}
Petr Machata cb74839
 
Petr Machata cb74839
-	if (arg_num == -1) {	/* return value */
Petr Machata cb74839
-		return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0);
Petr Machata cb74839
-	}
Petr Machata 4b23d2c
+/* Addresses for calling Thumb functions have the bit 0 set.
Petr Machata 4b23d2c
+   Here are some macros to test, set, or clear bit 0 of addresses.  */
Petr Machata 4b23d2c
+/* XXX double cast */
Petr Machata 4b23d2c
+#define IS_THUMB_ADDR(addr)	((uintptr_t)(addr) & 1)
Petr Machata 4b23d2c
+#define MAKE_THUMB_ADDR(addr)	((arch_addr_t)((uintptr_t)(addr) | 1))
Petr Machata 4b23d2c
+#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1))
Petr Machata cb74839
 
Petr Machata cb74839
-	/* deal with the ARM calling conventions */
Petr Machata cb74839
-	if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
Petr Machata cb74839
-		if (arg_num < 4) {
Petr Machata cb74839
-			if (a->valid && type == LT_TOF_FUNCTION)
Petr Machata cb74839
-				return a->regs.uregs[arg_num];
Petr Machata cb74839
-			if (a->valid && type == LT_TOF_FUNCTIONR)
Petr Machata cb74839
-				return a->func_arg[arg_num];
Petr Machata cb74839
-			return ptrace(PTRACE_PEEKUSER, proc->pid,
Petr Machata cb74839
-				      (void *)(4 * arg_num), 0);
Petr Machata cb74839
-		} else {
Petr Machata cb74839
-			return ptrace(PTRACE_PEEKDATA, proc->pid,
Petr Machata cb74839
-				      proc->stack_pointer + 4 * (arg_num - 4),
Petr Machata cb74839
-				      0);
Petr Machata 4b23d2c
+enum {
Petr Machata 4b23d2c
+	COND_ALWAYS = 0xe,
Petr Machata 4b23d2c
+	COND_NV = 0xf,
Petr Machata 4b23d2c
+	FLAG_C = 0x20000000,
Petr Machata 4b23d2c
+};
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+static int
Petr Machata 4b23d2c
+arm_get_next_pcs(struct process *proc,
Petr Machata 4b23d2c
+		 const arch_addr_t pc, arch_addr_t next_pcs[2])
Petr Machata 4b23d2c
+{
Petr Machata 4b23d2c
+	uint32_t this_instr;
Petr Machata 4b23d2c
+	uint32_t status;
Petr Machata 4b23d2c
+	if (proc_read_32(proc, pc, &this_instr) < 0
Petr Machata 4b23d2c
+	    || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
Petr Machata 4b23d2c
+		return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	/* In theory, we sometimes don't even need to add any
Petr Machata 4b23d2c
+	 * breakpoints at all.  If the conditional bits of the
Petr Machata 4b23d2c
+	 * instruction indicate that it should not be taken, then we
Petr Machata 4b23d2c
+	 * can just skip it altogether without bothering.  We could
Petr Machata 4b23d2c
+	 * also emulate the instruction under the breakpoint.
Petr Machata 4b23d2c
+	 *
Petr Machata 4b23d2c
+	 * Here, we make it as simple as possible (though We Accept
Petr Machata 4b23d2c
+	 * Patches).  */
Petr Machata 4b23d2c
+	int nr = 0;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	/* ARM can branch either relatively by using a branch
Petr Machata 4b23d2c
+	 * instruction, or absolutely, by doing arbitrary arithmetic
Petr Machata 4b23d2c
+	 * with PC as the destination.  */
Petr Machata 4b23d2c
+	const unsigned cond = BITS(this_instr, 28, 31);
Petr Machata 4b23d2c
+	const unsigned opcode = BITS(this_instr, 24, 27);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	if (cond == COND_NV)
Petr Machata 4b23d2c
+		switch (opcode) {
Petr Machata 4b23d2c
+			arch_addr_t addr;
Petr Machata 4b23d2c
+		case 0xa:
Petr Machata 4b23d2c
+		case 0xb:
Petr Machata 4b23d2c
+			/* Branch with Link and change to Thumb.  */
Petr Machata 4b23d2c
+			/* XXX double cast.  */
Petr Machata 4b23d2c
+			addr = (arch_addr_t)
Petr Machata 4b23d2c
+				((uint32_t)arm_branch_dest(pc, this_instr)
Petr Machata 4b23d2c
+				 | (((this_instr >> 24) & 0x1) << 1));
Petr Machata 4b23d2c
+			next_pcs[nr++] = MAKE_THUMB_ADDR(addr);
Petr Machata 4b23d2c
+			break;
Petr Machata cb74839
 		}
Petr Machata cb74839
-	} else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
Petr Machata cb74839
-		if (arg_num < 5) {
Petr Machata cb74839
-			if (a->valid && type == LT_TOF_SYSCALL)
Petr Machata cb74839
-				return a->regs.uregs[arg_num];
Petr Machata cb74839
-			if (a->valid && type == LT_TOF_SYSCALLR)
Petr Machata cb74839
-				return a->sysc_arg[arg_num];
Petr Machata cb74839
-			return ptrace(PTRACE_PEEKUSER, proc->pid,
Petr Machata cb74839
-				      (void *)(4 * arg_num), 0);
Petr Machata cb74839
-		} else {
Petr Machata cb74839
-			return ptrace(PTRACE_PEEKDATA, proc->pid,
Petr Machata cb74839
-				      proc->stack_pointer + 4 * (arg_num - 5),
Petr Machata cb74839
-				      0);
Petr Machata 4b23d2c
+	else
Petr Machata 4b23d2c
+		switch (opcode) {
Petr Machata 4b23d2c
+			uint32_t operand1, operand2, result = 0;
Petr Machata 4b23d2c
+		case 0x0:
Petr Machata 4b23d2c
+		case 0x1:			/* data processing */
Petr Machata 4b23d2c
+		case 0x2:
Petr Machata 4b23d2c
+		case 0x3:
Petr Machata 4b23d2c
+			if (BITS(this_instr, 12, 15) != ARM_REG_PC)
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if (BITS(this_instr, 22, 25) == 0
Petr Machata 4b23d2c
+			    && BITS(this_instr, 4, 7) == 9) {	/* multiply */
Petr Machata 4b23d2c
+			invalid:
Petr Machata 4b23d2c
+				fprintf(stderr,
Petr Machata 4b23d2c
+				"Invalid update to pc in instruction.\n");
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			/* BX <reg>, BLX <reg> */
Petr Machata 4b23d2c
+			if (BITS(this_instr, 4, 27) == 0x12fff1
Petr Machata 4b23d2c
+			    || BITS(this_instr, 4, 27) == 0x12fff3) {
Petr Machata 4b23d2c
+				enum arm_register reg = BITS(this_instr, 0, 3);
Petr Machata 4b23d2c
+				/* XXX double cast: no need to go
Petr Machata 4b23d2c
+				 * through tmp.  */
Petr Machata 4b23d2c
+				uint32_t tmp;
Petr Machata 4b23d2c
+				if (arm_get_register_offpc(proc, reg, &tmp) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				next_pcs[nr++] = (arch_addr_t)tmp;
Petr Machata 4b23d2c
+				return 0;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			/* Multiply into PC.  */
Petr Machata 4b23d2c
+			if (arm_get_register_offpc
Petr Machata 4b23d2c
+			    (proc, BITS(this_instr, 16, 19), &operand1) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			int c = (status & FLAG_C) ? 1 : 0;
Petr Machata 4b23d2c
+			if (BIT(this_instr, 25)) {
Petr Machata 4b23d2c
+				uint32_t immval = BITS(this_instr, 0, 7);
Petr Machata 4b23d2c
+				uint32_t rotate = 2 * BITS(this_instr, 8, 11);
Petr Machata 4b23d2c
+				operand2 = (((immval >> rotate)
Petr Machata 4b23d2c
+					     | (immval << (32 - rotate)))
Petr Machata 4b23d2c
+					    & 0xffffffff);
Petr Machata 4b23d2c
+			} else {
Petr Machata 4b23d2c
+				/* operand 2 is a shifted register.  */
Petr Machata 4b23d2c
+				if (arm_get_shifted_register
Petr Machata 4b23d2c
+				    (proc, this_instr, c, pc, &operand2) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			switch (BITS(this_instr, 21, 24)) {
Petr Machata 4b23d2c
+			case 0x0:	/*and */
Petr Machata 4b23d2c
+				result = operand1 & operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x1:	/*eor */
Petr Machata 4b23d2c
+				result = operand1 ^ operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x2:	/*sub */
Petr Machata 4b23d2c
+				result = operand1 - operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x3:	/*rsb */
Petr Machata 4b23d2c
+				result = operand2 - operand1;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x4:	/*add */
Petr Machata 4b23d2c
+				result = operand1 + operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x5:	/*adc */
Petr Machata 4b23d2c
+				result = operand1 + operand2 + c;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x6:	/*sbc */
Petr Machata 4b23d2c
+				result = operand1 - operand2 + c;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x7:	/*rsc */
Petr Machata 4b23d2c
+				result = operand2 - operand1 + c;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0x8:
Petr Machata 4b23d2c
+			case 0x9:
Petr Machata 4b23d2c
+			case 0xa:
Petr Machata 4b23d2c
+			case 0xb:	/* tst, teq, cmp, cmn */
Petr Machata 4b23d2c
+				/* Only take the default branch.  */
Petr Machata 4b23d2c
+				result = 0;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0xc:	/*orr */
Petr Machata 4b23d2c
+				result = operand1 | operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0xd:	/*mov */
Petr Machata 4b23d2c
+				/* Always step into a function.  */
Petr Machata 4b23d2c
+				result = operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0xe:	/*bic */
Petr Machata 4b23d2c
+				result = operand1 & ~operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			case 0xf:	/*mvn */
Petr Machata 4b23d2c
+				result = ~operand2;
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			/* XXX double cast */
Petr Machata 4b23d2c
+			next_pcs[nr++] = (arch_addr_t)result;
Petr Machata 4b23d2c
+			break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+		case 0x4:
Petr Machata 4b23d2c
+		case 0x5:		/* data transfer */
Petr Machata 4b23d2c
+		case 0x6:
Petr Machata 4b23d2c
+		case 0x7:
Petr Machata 4b23d2c
+			/* Ignore if insn isn't load or Rn not PC.  */
Petr Machata 4b23d2c
+			if (!BIT(this_instr, 20)
Petr Machata 4b23d2c
+			    || BITS(this_instr, 12, 15) != ARM_REG_PC)
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if (BIT(this_instr, 22))
Petr Machata 4b23d2c
+				goto invalid;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			/* byte write to PC */
Petr Machata 4b23d2c
+			uint32_t base;
Petr Machata 4b23d2c
+			if (arm_get_register_offpc
Petr Machata 4b23d2c
+			    (proc, BITS(this_instr, 16, 19), &base) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if (BIT(this_instr, 24)) {
Petr Machata 4b23d2c
+				/* pre-indexed */
Petr Machata 4b23d2c
+				int c = (status & FLAG_C) ? 1 : 0;
Petr Machata 4b23d2c
+				uint32_t offset;
Petr Machata 4b23d2c
+				if (BIT(this_instr, 25)) {
Petr Machata 4b23d2c
+					if (arm_get_shifted_register
Petr Machata 4b23d2c
+					    (proc, this_instr, c,
Petr Machata 4b23d2c
+					     pc, &offset) < 0)
Petr Machata 4b23d2c
+						return -1;
Petr Machata 4b23d2c
+				} else {
Petr Machata 4b23d2c
+					offset = BITS(this_instr, 0, 11);
Petr Machata 4b23d2c
+				}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				if (BIT(this_instr, 23))
Petr Machata 4b23d2c
+					base += offset;
Petr Machata 4b23d2c
+				else
Petr Machata 4b23d2c
+					base -= offset;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			/* XXX two double casts.  */
Petr Machata 4b23d2c
+			uint32_t next;
Petr Machata 4b23d2c
+			if (proc_read_32(proc, (arch_addr_t)base, &next) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+			next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+			break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+		case 0x8:
Petr Machata 4b23d2c
+		case 0x9:		/* block transfer */
Petr Machata 4b23d2c
+			if (!BIT(this_instr, 20))
Petr Machata 4b23d2c
+				break;
Petr Machata 4b23d2c
+			/* LDM */
Petr Machata 4b23d2c
+			if (BIT(this_instr, 15)) {
Petr Machata 4b23d2c
+				/* Loading pc.  */
Petr Machata 4b23d2c
+				int offset = 0;
Petr Machata 4b23d2c
+				enum arm_register rn = BITS(this_instr, 16, 19);
Petr Machata 4b23d2c
+				uint32_t rn_val;
Petr Machata 4b23d2c
+				if (arm_get_register(proc, rn, &rn_val) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				int pre = BIT(this_instr, 24);
Petr Machata 4b23d2c
+				if (BIT(this_instr, 23)) {
Petr Machata 4b23d2c
+					/* Bit U = up.  */
Petr Machata 4b23d2c
+					unsigned reglist
Petr Machata 4b23d2c
+						= BITS(this_instr, 0, 14);
Petr Machata 4b23d2c
+					offset = bitcount(reglist) * 4;
Petr Machata 4b23d2c
+					if (pre)
Petr Machata 4b23d2c
+						offset += 4;
Petr Machata 4b23d2c
+				} else if (pre) {
Petr Machata 4b23d2c
+					offset = -4;
Petr Machata 4b23d2c
+				}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				/* XXX double cast.  */
Petr Machata 4b23d2c
+				arch_addr_t addr
Petr Machata 4b23d2c
+					= (arch_addr_t)(rn_val + offset);
Petr Machata 4b23d2c
+				uint32_t next;
Petr Machata 4b23d2c
+				if (proc_read_32(proc, addr, &next) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+			break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+		case 0xb:		/* branch & link */
Petr Machata 4b23d2c
+		case 0xa:		/* branch */
Petr Machata 4b23d2c
+			next_pcs[nr++] = arm_branch_dest(pc, this_instr);
Petr Machata 4b23d2c
+			break;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+		case 0xc:
Petr Machata 4b23d2c
+		case 0xd:
Petr Machata 4b23d2c
+		case 0xe:		/* coproc ops */
Petr Machata 4b23d2c
+		case 0xf:		/* SWI */
Petr Machata 4b23d2c
+			break;
Petr Machata 4b23d2c
+		}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	/* Otherwise take the next instruction.  */
Petr Machata 4b23d2c
+	if (cond != COND_ALWAYS || nr == 0)
Petr Machata 4b23d2c
+		next_pcs[nr++] = pc + 4;
Petr Machata 4b23d2c
+	return 0;
Petr Machata 4b23d2c
+}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+/* Return the size in bytes of the complete Thumb instruction whose
Petr Machata 4b23d2c
+ * first halfword is INST1.  */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+static int
Petr Machata 4b23d2c
+thumb_insn_size (unsigned short inst1)
Petr Machata 4b23d2c
+{
Petr Machata 4b23d2c
+  if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
Petr Machata 4b23d2c
+	  return 4;
Petr Machata 4b23d2c
+  else
Petr Machata 4b23d2c
+	  return 2;
Petr Machata 4b23d2c
+}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+static int
Petr Machata 4b23d2c
+thumb_get_next_pcs(struct process *proc,
Petr Machata 4b23d2c
+		   const arch_addr_t pc, arch_addr_t next_pcs[2])
Petr Machata 4b23d2c
+{
Petr Machata 4b23d2c
+	uint16_t inst1;
Petr Machata 4b23d2c
+	uint32_t status;
Petr Machata 4b23d2c
+	if (proc_read_16(proc, pc, &inst1) < 0
Petr Machata 4b23d2c
+	    || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
Petr Machata 4b23d2c
+		return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	int nr = 0;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	/* We currently ignore Thumb-2 conditional execution support
Petr Machata 4b23d2c
+	 * (the IT instruction).  No branches are allowed in IT block,
Petr Machata 4b23d2c
+	 * and it's not legal to jump in the middle of it, so unless
Petr Machata 4b23d2c
+	 * we need to singlestep through large swaths of code, which
Petr Machata 4b23d2c
+	 * we currently don't, we can ignore them.  */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	if ((inst1 & 0xff00) == 0xbd00)	{ /* pop {rlist, pc} */
Petr Machata 4b23d2c
+		/* Fetch the saved PC from the stack.  It's stored
Petr Machata 4b23d2c
+		 * above all of the other registers.  */
Petr Machata 4b23d2c
+		const unsigned offset = bitcount(BITS(inst1, 0, 7)) * 4;
Petr Machata 4b23d2c
+		uint32_t sp;
Petr Machata 4b23d2c
+		uint32_t next;
Petr Machata 4b23d2c
+		/* XXX two double casts */
Petr Machata 4b23d2c
+		if (arm_get_register(proc, ARM_REG_SP, &sp) < 0
Petr Machata 4b23d2c
+		    || proc_read_32(proc, (arch_addr_t)(sp + offset),
Petr Machata 4b23d2c
+				    &next) < 0)
Petr Machata 4b23d2c
+			return -1;
Petr Machata 4b23d2c
+		next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+	} else if ((inst1 & 0xf000) == 0xd000) { /* conditional branch */
Petr Machata 4b23d2c
+		const unsigned long cond = BITS(inst1, 8, 11);
Petr Machata 4b23d2c
+		if (cond != 0x0f) { /* SWI */
Petr Machata 4b23d2c
+			next_pcs[nr++] = pc + (SBITS(inst1, 0, 7) << 1);
Petr Machata 4b23d2c
+			if (cond == COND_ALWAYS)
Petr Machata 4b23d2c
+				return 0;
Petr Machata 4b23d2c
+		}
Petr Machata 4b23d2c
+	} else if ((inst1 & 0xf800) == 0xe000) { /* unconditional branch */
Petr Machata 4b23d2c
+		next_pcs[nr++] = pc + (SBITS(inst1, 0, 10) << 1);
Petr Machata 4b23d2c
+	} else if (thumb_insn_size(inst1) == 4) { /* 32-bit instruction */
Petr Machata 4b23d2c
+		unsigned short inst2;
Petr Machata 4b23d2c
+		if (proc_read_16(proc, pc + 2, &inst2) < 0)
Petr Machata 4b23d2c
+			return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+		if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) {
Petr Machata 4b23d2c
+			/* Branches and miscellaneous control instructions.  */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if ((inst2 & 0x1000) != 0
Petr Machata 4b23d2c
+			    || (inst2 & 0xd001) == 0xc000) {
Petr Machata 4b23d2c
+				/* B, BL, BLX.  */
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				const int imm1 = SBITS(inst1, 0, 10);
Petr Machata 4b23d2c
+				const unsigned imm2 = BITS(inst2, 0, 10);
Petr Machata 4b23d2c
+				const unsigned j1 = BIT(inst2, 13);
Petr Machata 4b23d2c
+				const unsigned j2 = BIT(inst2, 11);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				int32_t offset
Petr Machata 4b23d2c
+					= ((imm1 << 12) + (imm2 << 1));
Petr Machata 4b23d2c
+				offset ^= ((!j2) << 22) | ((!j1) << 23);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				/* XXX double cast */
Petr Machata 4b23d2c
+				uint32_t next = (uint32_t)(pc + offset);
Petr Machata 4b23d2c
+				/* For BLX make sure to clear the low bits.  */
Petr Machata 4b23d2c
+				if (BIT(inst2, 12) == 0)
Petr Machata 4b23d2c
+					next = next & 0xfffffffc;
Petr Machata 4b23d2c
+				/* XXX double cast */
Petr Machata 4b23d2c
+				next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+				return 0;
Petr Machata 4b23d2c
+			} else if (inst1 == 0xf3de
Petr Machata 4b23d2c
+				   && (inst2 & 0xff00) == 0x3f00) {
Petr Machata 4b23d2c
+				/* SUBS PC, LR, #imm8.  */
Petr Machata 4b23d2c
+				uint32_t next;
Petr Machata 4b23d2c
+				if (arm_get_register(proc, ARM_REG_LR,
Petr Machata 4b23d2c
+						     &next) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				next -= inst2 & 0x00ff;
Petr Machata 4b23d2c
+				/* XXX double cast */
Petr Machata 4b23d2c
+				next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+				return 0;
Petr Machata 4b23d2c
+			} else if ((inst2 & 0xd000) == 0x8000
Petr Machata 4b23d2c
+				   && (inst1 & 0x0380) != 0x0380) {
Petr Machata 4b23d2c
+				/* Conditional branch.  */
Petr Machata 4b23d2c
+				const int sign = SBITS(inst1, 10, 10);
Petr Machata 4b23d2c
+				const unsigned imm1 = BITS(inst1, 0, 5);
Petr Machata 4b23d2c
+				const unsigned imm2 = BITS(inst2, 0, 10);
Petr Machata 4b23d2c
+				const unsigned j1 = BIT(inst2, 13);
Petr Machata 4b23d2c
+				const unsigned j2 = BIT(inst2, 11);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+				int32_t offset = (sign << 20)
Petr Machata 4b23d2c
+					+ (j2 << 19) + (j1 << 18);
Petr Machata 4b23d2c
+				offset += (imm1 << 12) + (imm2 << 1);
Petr Machata 4b23d2c
+				next_pcs[nr++] = pc + offset;
Petr Machata 4b23d2c
+				if (BITS(inst1, 6, 9) == COND_ALWAYS)
Petr Machata 4b23d2c
+					return 0;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+		} else if ((inst1 & 0xfe50) == 0xe810) {
Petr Machata 4b23d2c
+			int load_pc = 1;
Petr Machata 4b23d2c
+			int offset;
Petr Machata 4b23d2c
+			const enum arm_register rn = BITS(inst1, 0, 3);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if (BIT(inst1, 7) && !BIT(inst1, 8)) {
Petr Machata 4b23d2c
+				/* LDMIA or POP */
Petr Machata 4b23d2c
+				if (!BIT(inst2, 15))
Petr Machata 4b23d2c
+					load_pc = 0;
Petr Machata 4b23d2c
+				offset = bitcount(inst2) * 4 - 4;
Petr Machata 4b23d2c
+			} else if (!BIT(inst1, 7) && BIT(inst1, 8)) {
Petr Machata 4b23d2c
+				/* LDMDB */
Petr Machata 4b23d2c
+				if (!BIT(inst2, 15))
Petr Machata 4b23d2c
+					load_pc = 0;
Petr Machata 4b23d2c
+				offset = -4;
Petr Machata 4b23d2c
+			} else if (BIT(inst1, 7) && BIT(inst1, 8)) {
Petr Machata 4b23d2c
+				/* RFEIA */
Petr Machata 4b23d2c
+				offset = 0;
Petr Machata 4b23d2c
+			} else if (!BIT(inst1, 7) && !BIT(inst1, 8)) {
Petr Machata 4b23d2c
+				/* RFEDB */
Petr Machata 4b23d2c
+				offset = -8;
Petr Machata 4b23d2c
+			} else {
Petr Machata 4b23d2c
+				load_pc = 0;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if (load_pc) {
Petr Machata 4b23d2c
+				uint32_t addr;
Petr Machata 4b23d2c
+				if (arm_get_register(proc, rn, &addr) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				arch_addr_t a = (arch_addr_t)(addr + offset);
Petr Machata 4b23d2c
+				uint32_t next;
Petr Machata 4b23d2c
+				if (proc_read_32(proc, a, &next) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				/* XXX double cast */
Petr Machata 4b23d2c
+				next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+		} else if ((inst1 & 0xffef) == 0xea4f
Petr Machata 4b23d2c
+			   && (inst2 & 0xfff0) == 0x0f00) {
Petr Machata 4b23d2c
+			/* MOV PC or MOVS PC.  */
Petr Machata 4b23d2c
+			const enum arm_register rn = BITS(inst2, 0, 3);
Petr Machata 4b23d2c
+			uint32_t next;
Petr Machata 4b23d2c
+			if (arm_get_register(proc, rn, &next) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+			/* XXX double cast */
Petr Machata 4b23d2c
+			next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+		} else if ((inst1 & 0xff70) == 0xf850
Petr Machata 4b23d2c
+			   && (inst2 & 0xf000) == 0xf000) {
Petr Machata 4b23d2c
+			/* LDR PC.  */
Petr Machata 4b23d2c
+			const enum arm_register rn = BITS(inst1, 0, 3);
Petr Machata 4b23d2c
+			uint32_t base;
Petr Machata 4b23d2c
+			if (arm_get_register(proc, rn, &base) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			int load_pc = 1;
Petr Machata 4b23d2c
+			if (rn == ARM_REG_PC) {
Petr Machata 4b23d2c
+				base = (base + 4) & ~(uint32_t)0x3;
Petr Machata 4b23d2c
+				if (BIT(inst1, 7))
Petr Machata 4b23d2c
+					base += BITS(inst2, 0, 11);
Petr Machata 4b23d2c
+				else
Petr Machata 4b23d2c
+					base -= BITS(inst2, 0, 11);
Petr Machata 4b23d2c
+			} else if (BIT(inst1, 7)) {
Petr Machata 4b23d2c
+				base += BITS(inst2, 0, 11);
Petr Machata 4b23d2c
+			} else if (BIT(inst2, 11)) {
Petr Machata 4b23d2c
+				if (BIT(inst2, 10)) {
Petr Machata 4b23d2c
+					if (BIT(inst2, 9))
Petr Machata 4b23d2c
+						base += BITS(inst2, 0, 7);
Petr Machata 4b23d2c
+					else
Petr Machata 4b23d2c
+						base -= BITS(inst2, 0, 7);
Petr Machata 4b23d2c
+				}
Petr Machata 4b23d2c
+			} else if ((inst2 & 0x0fc0) == 0x0000) {
Petr Machata 4b23d2c
+				const int shift = BITS(inst2, 4, 5);
Petr Machata 4b23d2c
+				const enum arm_register rm = BITS(inst2, 0, 3);
Petr Machata 4b23d2c
+				uint32_t v;
Petr Machata 4b23d2c
+				if (arm_get_register(proc, rm, &v) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				base += v << shift;
Petr Machata 4b23d2c
+			} else {
Petr Machata 4b23d2c
+				/* Reserved.  */
Petr Machata 4b23d2c
+				load_pc = 0;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			if (load_pc) {
Petr Machata 4b23d2c
+				/* xxx double casts */
Petr Machata 4b23d2c
+				uint32_t next;
Petr Machata 4b23d2c
+				if (proc_read_32(proc,
Petr Machata 4b23d2c
+						 (arch_addr_t)base, &next) < 0)
Petr Machata 4b23d2c
+					return -1;
Petr Machata 4b23d2c
+				next_pcs[nr++] = (arch_addr_t)next;
Petr Machata 4b23d2c
+			}
Petr Machata 4b23d2c
+		} else if ((inst1 & 0xfff0) == 0xe8d0
Petr Machata 4b23d2c
+			   && (inst2 & 0xfff0) == 0xf000) {
Petr Machata 4b23d2c
+			/* TBB.  */
Petr Machata 4b23d2c
+			const enum arm_register tbl_reg = BITS(inst1, 0, 3);
Petr Machata 4b23d2c
+			const enum arm_register off_reg = BITS(inst2, 0, 3);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			uint32_t table;
Petr Machata 4b23d2c
+			if (tbl_reg == ARM_REG_PC)
Petr Machata 4b23d2c
+				/* Regcache copy of PC isn't right yet.  */
Petr Machata 4b23d2c
+				/* XXX double cast */
Petr Machata 4b23d2c
+				table = (uint32_t)pc + 4;
Petr Machata 4b23d2c
+			else if (arm_get_register(proc, tbl_reg, &table) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			uint32_t offset;
Petr Machata 4b23d2c
+			if (arm_get_register(proc, off_reg, &offset) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			table += offset;
Petr Machata 4b23d2c
+			uint8_t length;
Petr Machata 4b23d2c
+			/* XXX double cast */
Petr Machata 4b23d2c
+			if (proc_read_8(proc, (arch_addr_t)table, &length) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			next_pcs[nr++] = pc + 2 * length;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+		} else if ((inst1 & 0xfff0) == 0xe8d0
Petr Machata 4b23d2c
+			   && (inst2 & 0xfff0) == 0xf010) {
Petr Machata 4b23d2c
+			/* TBH.  */
Petr Machata 4b23d2c
+			const enum arm_register tbl_reg = BITS(inst1, 0, 3);
Petr Machata 4b23d2c
+			const enum arm_register off_reg = BITS(inst2, 0, 3);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			uint32_t table;
Petr Machata 4b23d2c
+			if (tbl_reg == ARM_REG_PC)
Petr Machata 4b23d2c
+				/* Regcache copy of PC isn't right yet.  */
Petr Machata 4b23d2c
+				/* XXX double cast */
Petr Machata 4b23d2c
+				table = (uint32_t)pc + 4;
Petr Machata 4b23d2c
+			else if (arm_get_register(proc, tbl_reg, &table) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			uint32_t offset;
Petr Machata 4b23d2c
+			if (arm_get_register(proc, off_reg, &offset) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			table += 2 * offset;
Petr Machata 4b23d2c
+			uint16_t length;
Petr Machata 4b23d2c
+			/* XXX double cast */
Petr Machata 4b23d2c
+			if (proc_read_16(proc, (arch_addr_t)table, &length) < 0)
Petr Machata 4b23d2c
+				return -1;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+			next_pcs[nr++] = pc + 2 * length;
Petr Machata cb74839
 		}
Petr Machata cb74839
-	} else {
Petr Machata cb74839
-		fprintf(stderr, "gimme_arg called with wrong arguments\n");
Petr Machata cb74839
-		exit(1);
Petr Machata cb74839
 	}
Petr Machata cb74839
 
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	/* Otherwise take the next instruction.  */
Petr Machata 4b23d2c
+	if (nr == 0)
Petr Machata 4b23d2c
+		next_pcs[nr++] = pc + thumb_insn_size(inst1);
Petr Machata cb74839
 	return 0;
Petr Machata cb74839
 }
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+enum sw_singlestep_status
Petr Machata 4b23d2c
+arch_sw_singlestep(struct process *proc, struct breakpoint *sbp,
Petr Machata 4b23d2c
+		   int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
Petr Machata 4b23d2c
+		   struct sw_singlestep_data *add_cb_data)
Petr Machata 4b23d2c
+{
Petr Machata 4b23d2c
+	const arch_addr_t pc = get_instruction_pointer(proc);
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	uint32_t cpsr;
Petr Machata 4b23d2c
+	if (arm_get_register(proc, ARM_REG_CPSR, &cpsr) < 0)
Petr Machata 4b23d2c
+		return SWS_FAIL;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	const unsigned thumb_p = BIT(cpsr, 5);
Petr Machata 4b23d2c
+	arch_addr_t next_pcs[2] = {};
Petr Machata 4b23d2c
+	if ((thumb_p ? &thumb_get_next_pcs
Petr Machata 4b23d2c
+	     : &arm_get_next_pcs)(proc, pc, next_pcs) < 0)
Petr Machata 4b23d2c
+		return SWS_FAIL;
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	int i;
Petr Machata 4b23d2c
+	for (i = 0; i < 2; ++i) {
Petr Machata 4b23d2c
+		/* XXX double cast.  */
Petr Machata 4b23d2c
+		arch_addr_t target
Petr Machata 4b23d2c
+			= (arch_addr_t)(((uintptr_t)next_pcs[i]) | thumb_p);
Petr Machata 4b23d2c
+		if (next_pcs[i] != 0 && add_cb(target, add_cb_data) < 0)
Petr Machata 4b23d2c
+			return SWS_FAIL;
Petr Machata 4b23d2c
+	}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+	debug(1, "PTRACE_CONT");
Petr Machata 4b23d2c
+	ptrace(PTRACE_CONT, proc->pid, 0, 0);
Petr Machata 4b23d2c
+	return SWS_OK;
Petr Machata 4b23d2c
+}
Petr Machata cb74839
+
Petr Machata cb74839
+size_t
Petr Machata cb74839
+arch_type_sizeof(struct process *proc, struct arg_type_info *info)
Petr Machata cb74839
+{
Petr Machata cb74839
+	if (proc == NULL)
Petr Machata cb74839
+		return (size_t)-2;
Petr Machata cb74839
+
Petr Machata cb74839
+	switch (info->type) {
Petr Machata cb74839
+	case ARGTYPE_VOID:
Petr Machata cb74839
+		return 0;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_CHAR:
Petr Machata cb74839
+		return 1;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_SHORT:
Petr Machata cb74839
+	case ARGTYPE_USHORT:
Petr Machata cb74839
+		return 2;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_INT:
Petr Machata cb74839
+	case ARGTYPE_UINT:
Petr Machata cb74839
+	case ARGTYPE_LONG:
Petr Machata cb74839
+	case ARGTYPE_ULONG:
Petr Machata cb74839
+	case ARGTYPE_POINTER:
Petr Machata cb74839
+		return 4;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_FLOAT:
Petr Machata cb74839
+		return 4;
Petr Machata cb74839
+	case ARGTYPE_DOUBLE:
Petr Machata cb74839
+		return 8;
Petr Machata cb74839
+
Petr Machata cb74839
+	case ARGTYPE_ARRAY:
Petr Machata cb74839
+	case ARGTYPE_STRUCT:
Petr Machata cb74839
+		/* Use default value.  */
Petr Machata cb74839
+		return (size_t)-2;
Petr Machata cb74839
+
Petr Machata cb74839
+	default:
Petr Machata cb74839
+		assert(info->type != info->type);
Petr Machata cb74839
+		abort();
Petr Machata cb74839
+	}
Petr Machata cb74839
+}
Petr Machata cb74839
+
Petr Machata cb74839
+size_t
Petr Machata cb74839
+arch_type_alignof(struct process *proc, struct arg_type_info *info)
Petr Machata cb74839
+{
Petr Machata cb74839
+	return arch_type_sizeof(proc, info);
Petr Machata cb74839
+}
Petr Machata cb74839
diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c
Petr Machata cb74839
index e90dbed..171c7a2 100644
Petr Machata cb74839
--- a/sysdeps/linux-gnu/ia64/fetch.c
Petr Machata cb74839
+++ b/sysdeps/linux-gnu/ia64/fetch.c
Petr Machata cb74839
@@ -1,6 +1,6 @@
Petr Machata cb74839
 /*
Petr Machata cb74839
  * This file is part of ltrace.
Petr Machata cb74839
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
Petr Machata cb74839
+ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc.
Petr Machata cb74839
  * Copyright (C) 2008,2009 Juan Cespedes
Petr Machata cb74839
  * Copyright (C) 2006 Steve Fink
Petr Machata cb74839
  * Copyright (C) 2006 Ian Wienand
Petr Machata cb74839
@@ -249,37 +249,6 @@ allocate_float(struct fetch_context *ctx, struct process *proc,
Petr Machata cb74839
 	return 0;
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
-static enum arg_type
Petr Machata cb74839
-get_hfa_type(struct arg_type_info *info, size_t *countp)
Petr Machata cb74839
-{
Petr Machata cb74839
-	size_t n = type_aggregate_size(info);
Petr Machata cb74839
-	if (n == (size_t)-1)
Petr Machata cb74839
-		return ARGTYPE_VOID;
Petr Machata cb74839
-
Petr Machata cb74839
-	enum arg_type type = ARGTYPE_VOID;
Petr Machata cb74839
-	*countp = 0;
Petr Machata cb74839
-
Petr Machata cb74839
-	while (n-- > 0) {
Petr Machata cb74839
-		struct arg_type_info *emt = type_element(info, n);
Petr Machata cb74839
-
Petr Machata cb74839
-		enum arg_type emt_type = emt->type;
Petr Machata cb74839
-		size_t emt_count = 1;
Petr Machata cb74839
-		if (emt_type == ARGTYPE_STRUCT || emt_type == ARGTYPE_ARRAY)
Petr Machata cb74839
-			emt_type = get_hfa_type(emt, &emt_count);
Petr Machata cb74839
-
Petr Machata cb74839
-		if (type == ARGTYPE_VOID) {
Petr Machata cb74839
-			if (emt_type != ARGTYPE_FLOAT
Petr Machata cb74839
-			    && emt_type != ARGTYPE_DOUBLE)
Petr Machata cb74839
-				return ARGTYPE_VOID;
Petr Machata cb74839
-			type = emt_type;
Petr Machata cb74839
-		}
Petr Machata cb74839
-		if (emt_type != type)
Petr Machata cb74839
-			return ARGTYPE_VOID;
Petr Machata cb74839
-		*countp += emt_count;
Petr Machata cb74839
-	}
Petr Machata cb74839
-	return type;
Petr Machata cb74839
-}
Petr Machata cb74839
-
Petr Machata cb74839
 static int
Petr Machata cb74839
 allocate_hfa(struct fetch_context *ctx, struct process *proc,
Petr Machata cb74839
 	     struct arg_type_info *info, struct value *valuep,
Petr Machata cb74839
@@ -380,10 +349,11 @@ allocate_ret(struct fetch_context *ctx, struct process *proc,
Petr Machata cb74839
 	 * floating-point registers, beginning with f8.  */
Petr Machata cb74839
 	if (info->type == ARGTYPE_STRUCT || info->type == ARGTYPE_ARRAY) {
Petr Machata cb74839
 		size_t hfa_size;
Petr Machata cb74839
-		enum arg_type hfa_type = get_hfa_type(info, &hfa_size);
Petr Machata cb74839
-		if (hfa_type != ARGTYPE_VOID && hfa_size <= 8)
Petr Machata cb74839
+		struct arg_type_info *hfa_info
Petr Machata cb74839
+			= type_get_hfa_type(info, &hfa_size);
Petr Machata cb74839
+		if (hfa_info != NULL && hfa_size <= 8)
Petr Machata cb74839
 			return allocate_hfa(ctx, proc, info, valuep,
Petr Machata cb74839
-					    hfa_type, hfa_size);
Petr Machata cb74839
+					    hfa_info->type, hfa_size);
Petr Machata cb74839
 	}
Petr Machata cb74839
 
Petr Machata cb74839
 	/* Integers and pointers are passed in r8.  128-bit integers
Petr Machata cb74839
@@ -409,7 +379,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
Petr Machata cb74839
 		    struct arg_type_info *info, struct value *valuep)
Petr Machata cb74839
 {
Petr Machata cb74839
 	switch (info->type) {
Petr Machata cb74839
-		enum arg_type hfa_type;
Petr Machata cb74839
+		struct arg_type_info *hfa_info;
Petr Machata cb74839
 		size_t hfa_size;
Petr Machata cb74839
 
Petr Machata cb74839
 	case ARGTYPE_VOID:
Petr Machata cb74839
@@ -421,10 +391,10 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
Petr Machata cb74839
 		return allocate_float(ctx, proc, info, valuep, 1);
Petr Machata cb74839
 
Petr Machata cb74839
 	case ARGTYPE_STRUCT:
Petr Machata cb74839
-		hfa_type = get_hfa_type(info, &hfa_size);
Petr Machata cb74839
-		if (hfa_type != ARGTYPE_VOID)
Petr Machata cb74839
+		hfa_info = type_get_hfa_type(info, &hfa_size);
Petr Machata cb74839
+		if (hfa_info != NULL)
Petr Machata cb74839
 			return allocate_hfa(ctx, proc, info, valuep,
Petr Machata cb74839
-					    hfa_type, hfa_size);
Petr Machata cb74839
+					    hfa_info->type, hfa_size);
Petr Machata cb74839
 		/* Fall through.  */
Petr Machata cb74839
 	case ARGTYPE_CHAR:
Petr Machata cb74839
 	case ARGTYPE_SHORT:
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c
Petr Machata 4b23d2c
index fb79e8a..67873ce 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/ia64/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/ia64/regs.c
Petr Machata 4b23d2c
@@ -1,6 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Ian Wienand
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
@@ -77,9 +77,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 		return NULL;
Petr Machata 4b23d2c
 	return (void *)l;
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKEUSER, proc->pid, PT_B0, addr);
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c
Petr Machata 4b23d2c
index c2fafe1..e25aefb 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/m68k/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/m68k/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
  * This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
@@ -58,9 +59,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c
Petr Machata 4b23d2c
index 19f97cb..d6a7a50 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/mipsel/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/mipsel/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc.
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
@@ -94,9 +95,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr);
Petr Machata 4b23d2c
-}
Petr Machata cb74839
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
Petr Machata cb74839
index 439b8e8..fe1602a 100644
Petr Machata cb74839
--- a/sysdeps/linux-gnu/ppc/plt.c
Petr Machata cb74839
+++ b/sysdeps/linux-gnu/ppc/plt.c
Petr Machata cb74839
@@ -262,7 +262,8 @@ load_opd_data(struct ltelf *lte, struct library *lib)
Petr Machata cb74839
 {
Petr Machata cb74839
 	Elf_Scn *sec;
Petr Machata cb74839
 	GElf_Shdr shdr;
Petr Machata cb74839
-	if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) {
Petr Machata cb74839
+	if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0
Petr Machata cb74839
+	    || sec == NULL) {
Petr Machata cb74839
 	fail:
Petr Machata cb74839
 		fprintf(stderr, "couldn't find .opd data\n");
Petr Machata cb74839
 		return -1;
Petr Machata cb74839
@@ -290,8 +291,9 @@ get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data)
Petr Machata cb74839
 	Elf_Scn *ppcgot_sec = NULL;
Petr Machata cb74839
 	GElf_Shdr ppcgot_shdr;
Petr Machata cb74839
 	if (ppcgot != 0
Petr Machata cb74839
-	    && elf_get_section_covering(lte, ppcgot,
Petr Machata cb74839
-					&ppcgot_sec, &ppcgot_shdr) < 0)
Petr Machata cb74839
+	    && (elf_get_section_covering(lte, ppcgot,
Petr Machata cb74839
+					 &ppcgot_sec, &ppcgot_shdr) < 0
Petr Machata cb74839
+		|| ppcgot_sec == NULL))
Petr Machata cb74839
 		fprintf(stderr,
Petr Machata cb74839
 			"DT_PPC_GOT=%#"PRIx64", but no such section found\n",
Petr Machata cb74839
 			ppcgot);
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c
Petr Machata 4b23d2c
index ed9b398..40d7e7a 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/ppc/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/ppc/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2002,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2008 Luis Machado, IBM Corporation
Petr Machata 4b23d2c
@@ -63,9 +64,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_LNK, 0);
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_LNK, addr);
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c
Petr Machata 4b23d2c
index 44e8f67..bb16c61 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/s390/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/s390/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2002,2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Ian Wienand
Petr Machata 4b23d2c
@@ -87,13 +88,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 #endif
Petr Machata 4b23d2c
 	return (void *)ret;
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-#ifdef __s390x__
Petr Machata 4b23d2c
-	if (proc->mask_32bit)
Petr Machata 4b23d2c
-		addr = (void *)((long)addr & PSW_MASK31);
Petr Machata 4b23d2c
-#endif
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKEUSER, proc->pid, PT_GPR14, addr);
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c
Petr Machata 4b23d2c
index 8431c9b..c474c83 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/sparc/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/sparc/regs.c
Petr Machata 4b23d2c
@@ -1,5 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Ian Wienand
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
@@ -65,12 +66,3 @@ get_return_addr(struct process *proc, void *stack_pointer)
Petr Machata 4b23d2c
 		return (void *)a->regs.u_regs[UREG_I6] + 12;
Petr Machata 4b23d2c
 	return (void *)a->regs.u_regs[UREG_I6] + 8;
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	proc_archdep *a = (proc_archdep *) (proc->arch_ptr);
Petr Machata 4b23d2c
-	if (!a->valid)
Petr Machata 4b23d2c
-		return;
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKETEXT, proc->pid, a->regs.u_regs[UREG_I6] + 8, addr);
Petr Machata 4b23d2c
-}
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
Petr Machata 4b23d2c
index e57a5ed..3aea082 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/trace.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/trace.c
Petr Machata 4b23d2c
@@ -561,12 +561,12 @@ remove_sw_breakpoints(struct process *proc)
Petr Machata 4b23d2c
 	assert(self != NULL);
Petr Machata 4b23d2c
 	assert(self->super.on_event == process_stopping_on_event);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-	int ct = sizeof(self->sws_bp_addrs) / sizeof(*self->sws_bp_addrs);
Petr Machata 4b23d2c
+	int ct = sizeof(self->sws_bps) / sizeof(*self->sws_bps);
Petr Machata 4b23d2c
 	int i;
Petr Machata 4b23d2c
 	for (i = 0; i < ct; ++i)
Petr Machata 4b23d2c
-		if (self->sws_bp_addrs[i] != 0) {
Petr Machata 4b23d2c
-			delete_breakpoint(proc, self->sws_bp_addrs[i]);
Petr Machata 4b23d2c
-			self->sws_bp_addrs[i] = 0;
Petr Machata 4b23d2c
+		if (self->sws_bps[i] != NULL) {
Petr Machata 4b23d2c
+			delete_breakpoint(proc, self->sws_bps[i]->addr);
Petr Machata 4b23d2c
+			self->sws_bps[i] = NULL;
Petr Machata 4b23d2c
 		}
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
@@ -586,18 +586,17 @@ sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data)
Petr Machata 4b23d2c
 	struct process_stopping_handler *self = data->self;
Petr Machata 4b23d2c
 	struct process *proc = self->task_enabling_breakpoint;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-	int ct = sizeof(self->sws_bp_addrs)
Petr Machata 4b23d2c
-		/ sizeof(*self->sws_bp_addrs);
Petr Machata 4b23d2c
+	int ct = sizeof(self->sws_bps) / sizeof(*self->sws_bps);
Petr Machata 4b23d2c
 	int i;
Petr Machata 4b23d2c
 	for (i = 0; i < ct; ++i)
Petr Machata 4b23d2c
-		if (self->sws_bp_addrs[i] == 0) {
Petr Machata 4b23d2c
-			self->sws_bp_addrs[i] = addr;
Petr Machata 4b23d2c
+		if (self->sws_bps[i] == NULL) {
Petr Machata 4b23d2c
 			static struct bp_callbacks cbs = {
Petr Machata 4b23d2c
 				.on_hit = sw_singlestep_bp_on_hit,
Petr Machata 4b23d2c
 			};
Petr Machata 4b23d2c
 			struct breakpoint *bp
Petr Machata 4b23d2c
 				= insert_breakpoint(proc, addr, NULL);
Petr Machata 4b23d2c
 			breakpoint_set_callbacks(bp, &cbs);
Petr Machata 4b23d2c
+			self->sws_bps[i] = bp;
Petr Machata 4b23d2c
 			return 0;
Petr Machata 4b23d2c
 		}
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
@@ -608,7 +607,9 @@ sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data)
Petr Machata 4b23d2c
 static int
Petr Machata 4b23d2c
 singlestep(struct process_stopping_handler *self)
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
-	struct process *proc = self->task_enabling_breakpoint;
Petr Machata 4b23d2c
+	size_t i;
Petr Machata 4b23d2c
+	for (i = 0; i < sizeof(self->sws_bps) / sizeof(*self->sws_bps); ++i)
Petr Machata 4b23d2c
+		self->sws_bps[i] = NULL;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	struct sw_singlestep_data data = { self };
Petr Machata 4b23d2c
 	switch (arch_sw_singlestep(self->task_enabling_breakpoint,
Petr Machata 4b23d2c
@@ -617,7 +618,8 @@ singlestep(struct process_stopping_handler *self)
Petr Machata 4b23d2c
 	case SWS_HW:
Petr Machata 4b23d2c
 		/* Otherwise do the default action: singlestep.  */
Petr Machata 4b23d2c
 		debug(1, "PTRACE_SINGLESTEP");
Petr Machata 4b23d2c
-		if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) {
Petr Machata 4b23d2c
+		if (ptrace(PTRACE_SINGLESTEP,
Petr Machata 4b23d2c
+			   self->task_enabling_breakpoint->pid, 0, 0)) {
Petr Machata 4b23d2c
 			perror("PTRACE_SINGLESTEP");
Petr Machata 4b23d2c
 			return -1;
Petr Machata 4b23d2c
 		}
Petr Machata 4b23d2c
@@ -1038,7 +1040,7 @@ ltrace_exiting_install_handler(struct process *proc)
Petr Machata 4b23d2c
 struct process_vfork_handler
Petr Machata 4b23d2c
 {
Petr Machata 4b23d2c
 	struct event_handler super;
Petr Machata 4b23d2c
-	void *bp_addr;
Petr Machata 4b23d2c
+	int vfork_bp_refd:1;
Petr Machata 4b23d2c
 };
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 static Event *
Petr Machata 4b23d2c
@@ -1049,38 +1051,33 @@ process_vfork_on_event(struct event_handler *super, Event *event)
Petr Machata 4b23d2c
 	      event->proc->pid, event->type);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	struct process_vfork_handler *self = (void *)super;
Petr Machata 4b23d2c
-	struct breakpoint *sbp;
Petr Machata 4b23d2c
+	struct process *proc = event->proc;
Petr Machata 4b23d2c
 	assert(self != NULL);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	switch (event->type) {
Petr Machata 4b23d2c
 	case EVENT_BREAKPOINT:
Petr Machata 4b23d2c
-		/* Remember the vfork return breakpoint.  */
Petr Machata 4b23d2c
-		if (self->bp_addr == 0)
Petr Machata 4b23d2c
-			self->bp_addr = event->e_un.brk_addr;
Petr Machata 4b23d2c
+		/* We turn on the vfork return breakpoint (which
Petr Machata 4b23d2c
+		 * should be the one that we have tripped over just
Petr Machata 4b23d2c
+		 * now) one extra time, so that the vfork parent hits
Petr Machata 4b23d2c
+		 * it as well.  */
Petr Machata 4b23d2c
+		if (!self->vfork_bp_refd) {
Petr Machata 4b23d2c
+			struct breakpoint *const sbp =
Petr Machata 4b23d2c
+				dict_find_entry(proc->leader->breakpoints,
Petr Machata 4b23d2c
+						event->e_un.brk_addr);
Petr Machata 4b23d2c
+			assert(sbp != NULL);
Petr Machata 4b23d2c
+			breakpoint_turn_on(sbp, proc->leader);
Petr Machata 4b23d2c
+			self->vfork_bp_refd = 1;
Petr Machata 4b23d2c
+		}
Petr Machata 4b23d2c
 		break;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	case EVENT_EXIT:
Petr Machata 4b23d2c
 	case EVENT_EXIT_SIGNAL:
Petr Machata 4b23d2c
 	case EVENT_EXEC:
Petr Machata 4b23d2c
-		/* Smuggle back in the vfork return breakpoint, so
Petr Machata 4b23d2c
-		 * that our parent can trip over it once again.  */
Petr Machata 4b23d2c
-		if (self->bp_addr != 0) {
Petr Machata 4b23d2c
-			sbp = dict_find_entry(event->proc->leader->breakpoints,
Petr Machata 4b23d2c
-					      self->bp_addr);
Petr Machata 4b23d2c
-			if (sbp != NULL)
Petr Machata 4b23d2c
-				assert(sbp->libsym == NULL);
Petr Machata 4b23d2c
-			/* We don't mind failing that, it's not a big
Petr Machata 4b23d2c
-			 * deal to not display one extra vfork return.  */
Petr Machata 4b23d2c
-			insert_breakpoint(event->proc->parent,
Petr Machata 4b23d2c
-					  self->bp_addr, NULL);
Petr Machata 4b23d2c
-		}
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-		continue_process(event->proc->parent->pid);
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
 		/* Remove the leader that we artificially set up
Petr Machata 4b23d2c
 		 * earlier.  */
Petr Machata 4b23d2c
-		change_process_leader(event->proc, event->proc);
Petr Machata 4b23d2c
-		destroy_event_handler(event->proc);
Petr Machata 4b23d2c
+		change_process_leader(proc, proc);
Petr Machata 4b23d2c
+		destroy_event_handler(proc);
Petr Machata 4b23d2c
+		continue_process(proc->parent->pid);
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	default:
Petr Machata 4b23d2c
 		;
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h
Petr Machata 4b23d2c
index e988f70..5bb8380 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/trace.h
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/trace.h
Petr Machata 4b23d2c
@@ -64,8 +64,8 @@ struct process_stopping_handler
Petr Machata 4b23d2c
 	/* The pointer being re-enabled.  */
Petr Machata 4b23d2c
 	struct breakpoint *breakpoint_being_enabled;
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-	/* Artificial atomic skip breakpoint, if any needed.  */
Petr Machata 4b23d2c
-	arch_addr_t sws_bp_addrs[2];
Petr Machata 4b23d2c
+	/* Software singlestep breakpoints, if any needed.  */
Petr Machata 4b23d2c
+	struct breakpoint *sws_bps[2];
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 	/* When all tasks are stopped, this callback gets called.  */
Petr Machata 4b23d2c
 	void (*on_all_stopped)(struct process_stopping_handler *);
Petr Machata 4b23d2c
diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c
Petr Machata 4b23d2c
index 3886e84..0a42c6e 100644
Petr Machata 4b23d2c
--- a/sysdeps/linux-gnu/x86/regs.c
Petr Machata 4b23d2c
+++ b/sysdeps/linux-gnu/x86/regs.c
Petr Machata 4b23d2c
@@ -1,6 +1,6 @@
Petr Machata 4b23d2c
 /*
Petr Machata 4b23d2c
  * This file is part of ltrace.
Petr Machata 4b23d2c
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
  * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
Petr Machata 4b23d2c
  * Copyright (C) 2006 Ian Wienand
Petr Machata 4b23d2c
  *
Petr Machata 4b23d2c
@@ -107,11 +107,3 @@ get_return_addr(struct process *proc, void *sp)
Petr Machata 4b23d2c
 		ret = conv_32(ret);
Petr Machata 4b23d2c
 	return ret;
Petr Machata 4b23d2c
 }
Petr Machata 4b23d2c
-
Petr Machata 4b23d2c
-void
Petr Machata 4b23d2c
-set_return_addr(struct process *proc, void *addr)
Petr Machata 4b23d2c
-{
Petr Machata 4b23d2c
-	if (proc->e_machine == EM_386)
Petr Machata 4b23d2c
-		addr = (void *)((long int)addr & 0xffffffff);
Petr Machata 4b23d2c
-	ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
Petr Machata 4b23d2c
-}
Petr Machata cb74839
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
Petr Machata cb74839
index e54086f..b585bc9 100644
Petr Machata cb74839
--- a/testsuite/ltrace.main/parameters.exp
Petr Machata cb74839
+++ b/testsuite/ltrace.main/parameters.exp
Petr Machata cb74839
@@ -35,9 +35,6 @@ if [regexp {ELF from incompatible architecture} $exec_output] {
Petr Machata cb74839
 	return
Petr Machata cb74839
 }
Petr Machata cb74839
 
Petr Machata cb74839
-set xfail_spec {"arm*-*" }
Petr Machata cb74839
-set xfail_spec_arm {"arm*-*"}
Petr Machata cb74839
-
Petr Machata cb74839
 # Verify the output
Petr Machata cb74839
 set pattern "func_intptr(17)"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
@@ -63,7 +60,6 @@ set pattern "func_ushort(33, 34)"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
 set pattern "func_float(3.40*, -3.40*).*= 3.40*"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
-eval "setup_xfail $xfail_spec"
Petr Machata cb74839
 set pattern "func_double(3.40*, -3.40*).*= -3.40*"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
 set pattern "func_typedef(BLUE)"
Petr Machata cb74839
@@ -86,7 +82,6 @@ set pattern "func_work(\\\"x\\\")"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
 set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
-eval "setup_xfail $xfail_spec_arm"
Petr Machata cb74839
 set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")"
Petr Machata cb74839
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
Petr Machata cb74839
 
Petr Machata 4b23d2c
diff --git a/testsuite/ltrace.torture/Makefile.am b/testsuite/ltrace.torture/Makefile.am
Petr Machata 4b23d2c
index daa772f..5a45265 100644
Petr Machata 4b23d2c
--- a/testsuite/ltrace.torture/Makefile.am
Petr Machata 4b23d2c
+++ b/testsuite/ltrace.torture/Makefile.am
Petr Machata 4b23d2c
@@ -15,15 +15,9 @@
Petr Machata 4b23d2c
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Petr Machata 4b23d2c
 #
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
-EXTRA_DIST = \
Petr Machata 4b23d2c
-	ia64-sigill.exp \
Petr Machata 4b23d2c
-	ia64-sigill.s \
Petr Machata 4b23d2c
-	ppc-lwarx.c \
Petr Machata 4b23d2c
-	ppc-lwarx.exp \
Petr Machata 4b23d2c
-	signals.c \
Petr Machata 4b23d2c
-	signals.exp \
Petr Machata 4b23d2c
-	vfork-thread.c \
Petr Machata 4b23d2c
-	vfork-thread.exp
Petr Machata 4b23d2c
+EXTRA_DIST = arm-singlestep.exp ia64-sigill.exp ia64-sigill.s	\
Petr Machata 4b23d2c
+	 ppc-lwarx.c ppc-lwarx.exp signals.c signals.exp	\
Petr Machata 4b23d2c
+	 vfork-thread.c vfork-thread.exp
Petr Machata 4b23d2c
 
Petr Machata 4b23d2c
 CLEANFILES = *.o *.so *.log *.sum *.ltrace setval.tmp \
Petr Machata 4b23d2c
 	signals
Petr Machata 4b23d2c
diff --git a/testsuite/ltrace.torture/arm-singlestep.exp b/testsuite/ltrace.torture/arm-singlestep.exp
Petr Machata 4b23d2c
new file mode 100644
Petr Machata 4b23d2c
index 0000000..0d633d9
Petr Machata 4b23d2c
--- /dev/null
Petr Machata 4b23d2c
+++ b/testsuite/ltrace.torture/arm-singlestep.exp
Petr Machata 4b23d2c
@@ -0,0 +1,44 @@
Petr Machata 4b23d2c
+# This file is part of ltrace.
Petr Machata 4b23d2c
+# Copyright (C) 2013 Petr Machata, Red Hat Inc.
Petr Machata 4b23d2c
+#
Petr Machata 4b23d2c
+# This program is free software; you can redistribute it and/or
Petr Machata 4b23d2c
+# modify it under the terms of the GNU General Public License as
Petr Machata 4b23d2c
+# published by the Free Software Foundation; either version 2 of the
Petr Machata 4b23d2c
+# License, or (at your option) any later version.
Petr Machata 4b23d2c
+#
Petr Machata 4b23d2c
+# This program is distributed in the hope that it will be useful, but
Petr Machata 4b23d2c
+# WITHOUT ANY WARRANTY; without even the implied warranty of
Petr Machata 4b23d2c
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Petr Machata 4b23d2c
+# General Public License for more details.
Petr Machata 4b23d2c
+#
Petr Machata 4b23d2c
+# You should have received a copy of the GNU General Public License
Petr Machata 4b23d2c
+# along with this program; if not, write to the Free Software
Petr Machata 4b23d2c
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Petr Machata 4b23d2c
+# 02110-1301 USA
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+if {![istarget arm*-*]} {
Petr Machata 4b23d2c
+    unsupported "arm-specific test"
Petr Machata 4b23d2c
+    return
Petr Machata 4b23d2c
+}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+set exe [ltraceCompile {} [ltraceSource c {
Petr Machata 4b23d2c
+    int puc(void) { return 0; }
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+    int bar(void);
Petr Machata 4b23d2c
+    int baz(void);
Petr Machata 4b23d2c
+    __asm__ ("	.type   bar, %function\n"
Petr Machata 4b23d2c
+	     "bar:		\n"
Petr Machata 4b23d2c
+	     "	b puc	\n"
Petr Machata 4b23d2c
+	     "	.type   baz, %function\n"
Petr Machata 4b23d2c
+	     "baz:		\n"
Petr Machata 4b23d2c
+	     "	b puc	\n");
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+    int main(void) { return bar() + baz(); }
Petr Machata 4b23d2c
+}]]
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+ltraceMatch [ltraceRun -L -xbar+baz $exe] {
Petr Machata 4b23d2c
+    {{bar} == 1}
Petr Machata 4b23d2c
+    {{baz} == 1}
Petr Machata 4b23d2c
+}
Petr Machata 4b23d2c
+
Petr Machata 4b23d2c
+ltraceDone
Petr Machata cb74839
diff --git a/type.c b/type.c
Petr Machata cb74839
index d80550b..e06a9c2 100644
Petr Machata cb74839
--- a/type.c
Petr Machata cb74839
+++ b/type.c
Petr Machata cb74839
@@ -1,6 +1,6 @@
Petr Machata cb74839
 /*
Petr Machata cb74839
  * This file is part of ltrace.
Petr Machata cb74839
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
Petr Machata cb74839
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata cb74839
  * Copyright (C) 2007,2008 Juan Cespedes
Petr Machata cb74839
  *
Petr Machata cb74839
  * This program is free software; you can redistribute it and/or
Petr Machata cb74839
@@ -568,3 +568,39 @@ type_get_fp_equivalent(struct arg_type_info *info)
Petr Machata cb74839
 	}
Petr Machata cb74839
 	abort();
Petr Machata cb74839
 }
Petr Machata cb74839
+
Petr Machata cb74839
+struct arg_type_info *
Petr Machata cb74839
+type_get_hfa_type(struct arg_type_info *info, size_t *countp)
Petr Machata cb74839
+{
Petr Machata cb74839
+	assert(info != NULL);
Petr Machata cb74839
+	if (info->type != ARGTYPE_STRUCT
Petr Machata cb74839
+	    && info->type != ARGTYPE_ARRAY)
Petr Machata cb74839
+		return NULL;
Petr Machata cb74839
+
Petr Machata cb74839
+	size_t n = type_aggregate_size(info);
Petr Machata cb74839
+	if (n == (size_t)-1)
Petr Machata cb74839
+		return NULL;
Petr Machata cb74839
+
Petr Machata cb74839
+	struct arg_type_info *ret = NULL;
Petr Machata cb74839
+	*countp = 0;
Petr Machata cb74839
+
Petr Machata cb74839
+	while (n-- > 0) {
Petr Machata cb74839
+		struct arg_type_info *emt = type_element(info, n);
Petr Machata cb74839
+
Petr Machata cb74839
+		size_t emt_count = 1;
Petr Machata cb74839
+		if (emt->type == ARGTYPE_STRUCT || emt->type == ARGTYPE_ARRAY)
Petr Machata cb74839
+			emt = type_get_hfa_type(emt, &emt_count);
Petr Machata cb74839
+		if (emt == NULL)
Petr Machata cb74839
+			return NULL;
Petr Machata cb74839
+		if (ret == NULL) {
Petr Machata cb74839
+			if (emt->type != ARGTYPE_FLOAT
Petr Machata cb74839
+			    && emt->type != ARGTYPE_DOUBLE)
Petr Machata cb74839
+				return NULL;
Petr Machata cb74839
+			ret = emt;
Petr Machata cb74839
+		}
Petr Machata cb74839
+		if (emt->type != ret->type)
Petr Machata cb74839
+			return NULL;
Petr Machata cb74839
+		*countp += emt_count;
Petr Machata cb74839
+	}
Petr Machata cb74839
+	return ret;
Petr Machata cb74839
+}
Petr Machata cb74839
diff --git a/type.h b/type.h
Petr Machata cb74839
index b92c1af..3210677 100644
Petr Machata cb74839
--- a/type.h
Petr Machata cb74839
+++ b/type.h
Petr Machata cb74839
@@ -1,6 +1,6 @@
Petr Machata cb74839
 /*
Petr Machata cb74839
  * This file is part of ltrace.
Petr Machata cb74839
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
Petr Machata cb74839
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
Petr Machata cb74839
  * Copyright (C) 1997-2009 Juan Cespedes
Petr Machata cb74839
  *
Petr Machata cb74839
  * This program is free software; you can redistribute it and/or
Petr Machata cb74839
@@ -142,4 +142,13 @@ int type_is_signed(enum arg_type type);
Petr Machata cb74839
  * type.  */
Petr Machata cb74839
 struct arg_type_info *type_get_fp_equivalent(struct arg_type_info *info);
Petr Machata cb74839
 
Petr Machata cb74839
+/* If INFO is homogeneous floating-point aggregate, return the
Petr Machata cb74839
+ * corresponding floating point type, and set *COUNTP to number of
Petr Machata cb74839
+ * fields of the structure.  Otherwise return NULL.  INFO is a HFA if
Petr Machata cb74839
+ * it's an aggregate whose each field is either a HFA, or a
Petr Machata cb74839
+ * floating-point type.  */
Petr Machata cb74839
+struct arg_type_info *type_get_hfa_type(struct arg_type_info *info,
Petr Machata cb74839
+					size_t *countp);
Petr Machata cb74839
+
Petr Machata cb74839
+
Petr Machata cb74839
 #endif /* TYPE_H */