diff options
author | Emmanuel Touron | 2013-03-25 09:24:00 +0000 |
---|---|---|
committer | Eugene Tarassov | 2013-03-27 18:45:01 +0000 |
commit | c84feeeda7451fdd0d12eb74d1086af608acd181 (patch) | |
tree | 4e1330c17455c3189e6389d4f74eae33d2acf561 | |
parent | 40f20cf8bd4c931f3079b4c5f6d6cac50c471853 (diff) | |
download | org.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.c | 208 | ||||
-rw-r--r-- | agent/machine/armv6l/tcf/cpudefs-mdep.h | 5 | ||||
-rw-r--r-- | agent/machine/armv6l/tcf/regset-mdep.h | 20 | ||||
-rw-r--r-- | agent/system/GNU/Linux/tcf/context-linux.c | 40 | ||||
-rw-r--r-- | agent/tcf/framework/cpudefs.c | 11 | ||||
-rw-r--r-- | agent/tcf/framework/cpudefs.h | 8 |
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); |