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