Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Touron2013-03-25 09:24:00 +0000
committerEugene Tarassov2013-03-27 18:45:01 +0000
commitc84feeeda7451fdd0d12eb74d1086af608acd181 (patch)
tree4e1330c17455c3189e6389d4f74eae33d2acf561
parent40f20cf8bd4c931f3079b4c5f6d6cac50c471853 (diff)
downloadorg.eclipse.tcf.agent-c84feeeda7451fdd0d12eb74d1086af608acd181.tar.gz
org.eclipse.tcf.agent-c84feeeda7451fdd0d12eb74d1086af608acd181.tar.xz
org.eclipse.tcf.agent-c84feeeda7451fdd0d12eb74d1086af608acd181.zip
TCF Agent: [404253] debug: initial armv6l stepping emulation
Added initial software emulation for single-stepping. This requires computing the next address for each instruction, and planting a breakpoint there. The next-address computation is incomplete at this time, but should work for basic debugging. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=404253 for details and discussion.
-rw-r--r--agent/machine/armv6l/tcf/cpudefs-mdep.c208
-rw-r--r--agent/machine/armv6l/tcf/cpudefs-mdep.h5
-rw-r--r--agent/machine/armv6l/tcf/regset-mdep.h20
-rw-r--r--agent/system/GNU/Linux/tcf/context-linux.c40
-rw-r--r--agent/tcf/framework/cpudefs.c11
-rw-r--r--agent/tcf/framework/cpudefs.h8
6 files changed, 260 insertions, 32 deletions
diff --git a/agent/machine/armv6l/tcf/cpudefs-mdep.c b/agent/machine/armv6l/tcf/cpudefs-mdep.c
index cf614830..63b0cb2a 100644
--- a/agent/machine/armv6l/tcf/cpudefs-mdep.c
+++ b/agent/machine/armv6l/tcf/cpudefs-mdep.c
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013 Stanislav Yakovlev.
+ * Copyright (c) 2013 Stanislav Yakovlev and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
@@ -11,6 +11,7 @@
*
* Contributors:
* Stanislav Yakovlev - initial API and implementation
+ * Emmanuel Touron (Wind River) - initial ARM stepping emulation
*******************************************************************************/
#include <tcf/config.h>
@@ -24,19 +25,19 @@
#include <tcf/framework/cpudefs.h>
#include <tcf/framework/context.h>
#include <tcf/framework/myalloc.h>
+#include <tcf/framework/trace.h>
#include <tcf/services/symbols.h>
#include <machine/arm/tcf/disassembler-arm.h>
#include <machine/arm/tcf/stack-crawl-arm.h>
#include <tcf/cpudefs-mdep.h>
-#if defined(__arm__)
-
#define REG_OFFSET(name) offsetof(REG_SET, name)
RegisterDefinition regs_def[] = {
# define REG_FP user.regs.uregs[11]
# define REG_SP user.regs.uregs[13]
# define REG_PC user.regs.uregs[15]
+# define REG_CPSR user.regs.uregs[16]
{ "r0", REG_OFFSET(user.regs.uregs[0]), 4, 0, 0},
{ "r1", REG_OFFSET(user.regs.uregs[1]), 4, 1, 1},
{ "r2", REG_OFFSET(user.regs.uregs[2]), 4, 2, 2},
@@ -55,32 +56,74 @@ RegisterDefinition regs_def[] = {
{ "pc", REG_OFFSET(user.regs.uregs[15]), 4, 15, 15},
{ "cpsr", REG_OFFSET(user.regs.uregs[16]), 4, -1, -1},
{ "orig_r0", REG_OFFSET(user.regs.uregs[17]), 4, -1, -1},
+ { "debug", 0, 0, -1, -1, 0, 0, 1, 1 }, /* 18 */
+ { "bp_info", REG_OFFSET(other.bp_info), 4, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, regs_def + 18},
+ { "bvr0", REG_OFFSET(other.bp[0].vr), 4, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, regs_def + 18},
+ { "bcr0", REG_OFFSET(other.bp[0].cr), 4, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, regs_def + 18},
{ NULL, 0, 0, 0, 0},
};
RegisterDefinition * regs_index = NULL;
-unsigned char BREAK_INST[] = { 0xf0, 0x01, 0xf0, 0xe7 };
+unsigned char BREAK_INST[] = { 0x7f, 0xff, 0x2f, 0xe1 };
static RegisterDefinition * pc_def = NULL;
+static RegisterDefinition * cpsr_def = NULL;
-void ini_cpudefs_mdep(void) {
- RegisterDefinition * r;
- for (r = regs_def; r->name != NULL; r++) {
- if (r->offset == offsetof(REG_SET, REG_FP)) {
- r->role = "FP";
- }
- else if (r->offset == offsetof(REG_SET, REG_SP)) {
- r->role = "SP";
- }
- else if (r->offset == offsetof(REG_SET, REG_PC)) {
- r->role = "PC";
- pc_def = r;
- }
+#ifdef MDEP_OtherRegisters
+
+#include <sys/ptrace.h>
+
+#if !defined(PTRACE_GETHBPREGS)
+#define PTRACE_GETHBPREGS 29
+#endif
+#if !defined(PTRACE_SETHBPREGS)
+#define PTRACE_SETHBPREGS 30
+#endif
+#define MAX_HWP 1
+static int offset_to_regnum(size_t offset) {
+ int bp_id = (offset - 4) >> 3;
+ int reg_offset = 1 + (((offset - 4) >> 2) & 1);
+ if (offset == 0) return 0;
+ trace (LOG_ALWAYS,"offset_to_regnum offset %x, bp_id %x, reg_offset %x", offset, bp_id, reg_offset);
+ if (bp_id >= MAX_HWP) return - (((bp_id - MAX_HWP) << 1) + reg_offset);
+ return (bp_id << 1) + reg_offset;
+}
+
+int mdep_get_other_regs(pid_t pid, REG_SET * data,
+ size_t data_offs, size_t data_size,
+ size_t * done_offs, size_t * done_size) {
+ size_t size = 0;
+ assert(data_offs >= offsetof(REG_SET, other));
+ assert(data_offs + data_size <= offsetof(REG_SET, other) + sizeof(data->other));
+ /* bp registers can only be accessed 1 at a time */
+ for (size = data_offs - offsetof(REG_SET, other); size < sizeof(data->other); size += 4) {
+ trace (LOG_ALWAYS,"get_other_registers %x", offset_to_regnum(size));
+ if (ptrace(PTRACE_GETHBPREGS, pid, offset_to_regnum (size), (char *)&data->other + size) < 0) return -1;
}
- regs_index = regs_def;
+ *done_offs = offsetof(REG_SET, other);
+ *done_size = sizeof(data->other);
+ return 0;
}
+int mdep_set_other_regs(pid_t pid, REG_SET * data,
+ size_t data_offs, size_t data_size,
+ size_t * done_offs, size_t * done_size) {
+ size_t size = 0;
+ assert(data_offs >= offsetof(REG_SET, other));
+ assert(data_offs + data_size <= offsetof(REG_SET, other) + sizeof(data->other));
+ /* bp registers can only be accessed 1 at a time */
+ for (size = data_offs - offsetof(REG_SET, other); size < data_offs + data_size - offsetof(REG_SET, other); size += 4) {
+ trace (LOG_ALWAYS,"set_other_registers %x", offset_to_regnum(size));
+ if (ptrace(PTRACE_SETHBPREGS, pid, offset_to_regnum(size), (char *)&data->other + size) < 0) return -1;
+ }
+ *done_offs = offsetof(REG_SET, other);
+ *done_size = sizeof(data->other);
+ return 0;
+}
+
+#endif
+
RegisterDefinition * get_PC_definition(Context * ctx) {
if (!context_has_state(ctx)) return NULL;
return pc_def;
@@ -95,5 +138,132 @@ void add_cpudefs_disassembler(Context * cpu_ctx) {
add_disassembler(cpu_ctx, "Thumb", disassemble_thumb);
}
-#endif
+static int read_reg(Context *ctx, RegisterDefinition * def, size_t size, ContextAddress * addr) {
+ size_t i;
+ uint8_t buf[8];
+ uint64_t n = 0;
+ *addr = 0;
+ assert(!def->big_endian);
+ assert(size <= def->size);
+ assert(size <= sizeof(buf));
+ if (context_read_reg(ctx, def, 0, size, buf) < 0) return -1;
+ for (i = 0; i < size; i++) n |= (uint64_t)buf[i] << (i * 8);
+ *addr = (ContextAddress)n;
+ return 0;
+}
+
+typedef struct ContextExtensionARM {
+ char opcode[sizeof(BREAK_INST)];
+ ContextAddress addr;
+ int stepping;
+} ContextExtensionARM;
+
+static size_t context_extension_offset = 0;
+
+#define EXT(ctx) ((ContextExtensionARM *)((char *)(ctx) + context_extension_offset))
+
+#define GET_GROUP(a) (((a) >> 25) & 7)
+#define BRANCH_LINK 5
+
+static int arm_evaluate_condition (uint32_t opc, uint32_t cpsr) {
+ int N = ( cpsr >> 31 ) & 1;
+ int Z = ( cpsr >> 30 ) & 1;
+ int C = ( cpsr >> 29 ) & 1;
+ int V = ( cpsr >> 28 ) & 1;
+
+ switch (opc >> 28) {
+ case 0 : return Z;
+ case 1 : return Z == 0;
+ case 2 : return C;
+ case 3 : return C == 0;
+ case 4 : return N;
+ case 5 : return N == 0;
+ case 6 : return V;
+ case 7 : return V == 0;
+ case 8 : return C == 0 && Z == 0;
+ case 9 : return C == 0 || Z == 1;
+ case 10: return N == V;
+ case 11: return N != V;
+ case 12: return Z == 0 && N == V;
+ case 13: return Z == 1 || N != V;
+ }
+
+ return 1;
+}
+
+static ContextAddress arm_get_next_branch (Context * ctx, ContextAddress addr, uint32_t opc, int cond) {
+ int32_t imm = opc & 0x00FFFFFF;
+ if (cond == 0) return addr + 4;
+ if (imm & 0x00800000) imm |= 0xFF000000;
+ imm = imm << 2;
+ return (ContextAddress)((int)addr + imm + 8);
+}
+
+static ContextAddress arm_get_next_address (Context * ctx) {
+ ContextAddress addr;
+ uint32_t opc;
+ ContextAddress cpsr;
+ int cond;
+
+ /* read opcode at PC */
+ if (read_reg(ctx, pc_def, pc_def->size, &addr) < 0) return -1;
+ if (read_reg(ctx, cpsr_def, cpsr_def->size, &cpsr) < 0) return -1;
+ if (context_read_mem(ctx, addr, &opc, sizeof(opc)) < 0) return -1;
+ trace (LOG_ALWAYS, "pc: 0x%x, opcode 0x%x", (int)addr, (int)opc);
+
+ /* decode opcode */
+ cond = arm_evaluate_condition (opc, (uint32_t) cpsr);
+
+ switch (GET_GROUP(opc)) {
+// case LD_ST_IMM : return get_next_load_store_imm ();
+ case BRANCH_LINK : return arm_get_next_branch (ctx, addr, opc, cond);
+ }
+ return addr + 4;
+}
+
+int cpu_enable_stepping_mode (Context * ctx, uint32_t * is_cont) {
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ ContextExtensionARM * ext = EXT(grp);
+ assert (!ext->stepping);
+ ext->addr = arm_get_next_address (ctx);
+ trace (LOG_ALWAYS, "cpu_enable_stepping_mode 0x%x", (int) ext->addr);
+ if (context_read_mem(ctx, ext->addr, ext->opcode, sizeof(BREAK_INST)) < 0) return -1;
+ if (context_write_mem(ctx,ext->addr, BREAK_INST, sizeof(BREAK_INST)) < 0) return -1;
+ ext->stepping = 1;
+ *is_cont = 1;
+ return 0;
+}
+
+int cpu_disable_stepping_mode (Context * ctx, ContextAddress unpatched_pc) {
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ ContextExtensionARM * ext = EXT(grp);
+ trace (LOG_ALWAYS, "cpu_disable_stepping_mode");
+ if (ext->stepping) {
+ ext->stepping = 0;
+ return context_write_mem(ctx, ext->addr, ext->opcode, sizeof(BREAK_INST));
+ }
+ return 0;
+}
+
+void ini_cpudefs_mdep(void) {
+ RegisterDefinition * r;
+ for (r = regs_def; r->name != NULL; r++) {
+ if (r->offset == offsetof(REG_SET, REG_FP)) {
+ r->role = "FP";
+ }
+ else if (r->offset == offsetof(REG_SET, REG_SP)) {
+ r->role = "SP";
+ }
+ else if (r->offset == offsetof(REG_SET, REG_PC)) {
+ r->role = "PC";
+ pc_def = r;
+ }
+ else if (r->offset == offsetof(REG_SET, REG_CPSR)) {
+ cpsr_def = r;
+ }
+ }
+ regs_index = regs_def;
+ context_extension_offset = context_extension(sizeof(ContextExtensionARM));
+}
+
#endif
diff --git a/agent/machine/armv6l/tcf/cpudefs-mdep.h b/agent/machine/armv6l/tcf/cpudefs-mdep.h
index 5c8e6504..9ac5637c 100644
--- a/agent/machine/armv6l/tcf/cpudefs-mdep.h
+++ b/agent/machine/armv6l/tcf/cpudefs-mdep.h
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013 Stanislav Yakovlev.
+ * Copyright (c) 2013 Stanislav Yakovlev and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
@@ -11,6 +11,7 @@
*
* Contributors:
* Stanislav Yakovlev - initial API and implementation
+ * Emmanuel Touron (Wind River) - initial ARM stepping emulation
*******************************************************************************/
/*
@@ -30,4 +31,6 @@ extern void ini_cpudefs_mdep(void);
#define ENABLE_add_cpudefs_disassembler 1
extern void add_cpudefs_disassembler(Context * cpu_ctx);
+#define ENABLE_external_stepping_mode 1
+
#endif
diff --git a/agent/machine/armv6l/tcf/regset-mdep.h b/agent/machine/armv6l/tcf/regset-mdep.h
index 204d69b3..b3b4c790 100644
--- a/agent/machine/armv6l/tcf/regset-mdep.h
+++ b/agent/machine/armv6l/tcf/regset-mdep.h
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013 Stanislav Yakovlev.
+ * Copyright (c) 2013 Stanislav Yakovlev and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
@@ -11,7 +11,23 @@
*
* Contributors:
* Stanislav Yakovlev - initial API and implementation
+ * Emmanuel Touron (Wind River) - initial HW Breakpoint support
*******************************************************************************/
/* offset to be applied to the PC after a software trap */
-#define TRAP_OFFSET -8
+#define TRAP_OFFSET 0
+
+#define MAX_HBP 1
+typedef struct {
+ uint32_t vr;
+ uint32_t cr;
+} user_hbpreg_struct;
+
+struct user_hbpregs_struct {
+ uint32_t bp_info;
+ user_hbpreg_struct bp[MAX_HBP];
+// user_hbpreg_struct wp[MAX_HBP];
+};
+
+/* additional CPU registers */
+#define MDEP_OtherRegisters struct user_hbpregs_struct
diff --git a/agent/system/GNU/Linux/tcf/context-linux.c b/agent/system/GNU/Linux/tcf/context-linux.c
index 4937857f..dcbf18dc 100644
--- a/agent/system/GNU/Linux/tcf/context-linux.c
+++ b/agent/system/GNU/Linux/tcf/context-linux.c
@@ -414,6 +414,7 @@ static void send_process_exited_event(Context * prs) {
}
static int do_single_step(Context * ctx) {
+ uint32_t is_cont;
ContextExtensionLinux * ext = EXT(ctx);
assert(!ext->pending_step);
@@ -421,20 +422,38 @@ static int do_single_step(Context * ctx) {
if (skip_breakpoint(ctx, 1)) return 0;
trace(LOG_CONTEXT, "context: single step ctx %#lx, id %s", ctx, ctx->id);
+ cpu_enable_stepping_mode(ctx, &is_cont);
if (flush_regs(ctx) < 0) return -1;
if (!ctx->stopped) return 0;
- if (ptrace(PTRACE_SINGLESTEP, ext->pid, 0, 0) < 0) {
- int err = errno;
- if (err == ESRCH) {
- ctx->exiting = 1;
- send_context_started_event(ctx);
- return 0;
+ if (is_cont) {
+ if (ptrace(PTRACE_CONT, ext->pid, 0, 0) < 0) {
+ int err = errno;
+ if (err == ESRCH) {
+ ctx->exiting = 1;
+ send_context_started_event(ctx);
+ return 0;
+ }
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_CONT, ...) failed: ctx %#lx, id %s, error %d %s",
+ ctx, ctx->id, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ }
+ else {
+ if (ptrace(PTRACE_SINGLESTEP, ext->pid, 0, 0) < 0) {
+ int err = errno;
+ if (err == ESRCH) {
+ ctx->exiting = 1;
+ send_context_started_event(ctx);
+ return 0;
+ }
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_SINGLESTEP, ...) failed: ctx %#lx, id %s, error %d %s",
+ ctx, ctx->id, err, errno_to_str(err));
+ errno = err;
+ return -1;
}
- trace(LOG_ALWAYS, "error: ptrace(PTRACE_SINGLESTEP, ...) failed: ctx %#lx, id %s, error %d %s",
- ctx, ctx->id, err, errno_to_str(err));
- errno = err;
- return -1;
}
+
ext->pending_step = 1;
send_context_started_event(ctx);
return 0;
@@ -1366,6 +1385,7 @@ static void event_pid_stopped(pid_t pid, int signal, int event, int syscall) {
else ctx->stopped_by_bp = is_breakpoint_address(ctx, pc1);
ext->end_of_step = !ctx->stopped_by_cb && !ctx->stopped_by_bp && ext->pending_step;
}
+ cpu_disable_stepping_mode(ctx, pc1);
ext->pending_step = 0;
send_context_stopped_event(ctx);
}
diff --git a/agent/tcf/framework/cpudefs.c b/agent/tcf/framework/cpudefs.c
index 2061471a..e0975e6a 100644
--- a/agent/tcf/framework/cpudefs.c
+++ b/agent/tcf/framework/cpudefs.c
@@ -579,6 +579,17 @@ void write_location_pieces(Context * ctx, StackFrame * frame,
}
}
+#if !defined(ENABLE_external_stepping_mode) || !ENABLE_external_stepping_mode
+int cpu_enable_stepping_mode (Context * ctx, uint32_t * is_cont) {
+ * is_cont = 0;
+ return 0;
+}
+
+int cpu_disable_stepping_mode (Context * ctx) {
+ return 0;
+}
+#endif
+
#if !defined(ENABLE_HardwareBreakpoints) || !ENABLE_HardwareBreakpoints
int cpu_bp_get_capabilities(Context * ctx) {
return 0;
diff --git a/agent/tcf/framework/cpudefs.h b/agent/tcf/framework/cpudefs.h
index 906ad773..8ad54eb2 100644
--- a/agent/tcf/framework/cpudefs.h
+++ b/agent/tcf/framework/cpudefs.h
@@ -316,6 +316,14 @@ extern int cpu_bp_on_resume(Context * ctx, int * single_step);
/* Check breakpoint registers for a context that has stopped */
extern int cpu_bp_on_suspend(Context * ctx, int * triggered);
+/*** CPU external stepping mode API ***/
+
+/* Disable the stepping mode */
+extern int cpu_disable_stepping_mode (Context * ctx, ContextAddress pc);
+
+/* Enable the stepping mode */
+extern int cpu_enable_stepping_mode (Context * ctx, uint32_t * is_cont);
+
/*** Initialization functions ***/
extern void ini_cpu_disassembler(Context * cpu);

Back to the top