summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Griffin <peter.griffin@linaro.org>2016-06-15 14:59:06 +0100
committerPeter Griffin <peter.griffin@linaro.org>2016-06-15 15:07:34 +0100
commitc9e4a8a1c9b60ce957cab14f8725b76dabf7e998 (patch)
tree6582ad6b978e7c5f94790a94879cf65f0694cb0a
parent3b1853a301bea9fc35859732277af4d0eb3f2e8a (diff)
gdb/arm-linux-tdep: Add arm_linux_supply_thread implementation
Populate the regcache for a given thread from what has been saved by Linux kernel schedule() onto the stack. This is done by determining the offset of cpu_context inside the given task_struct, and reading a cpu_cxt sized amount of memory from the target to populate the register cache. Any remaining registers are marked as invalid. Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
-rw-r--r--gdb/arm-linux-tdep.c65
1 files changed, 61 insertions, 4 deletions
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index cc29c790fd..ba2a75813d 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -1384,23 +1384,78 @@ arm_linux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
return find_solib_trampoline_target (frame, pc);
}
+/* Support for Linux kernel threads */
-
+/* From arm/include/asm/thread_info.h */
+static struct cpu_context_save
+{
+ uint32_t r4;
+ uint32_t r5;
+ uint32_t r6;
+ uint32_t r7;
+ uint32_t r8;
+ uint32_t r9;
+ uint32_t sl;
+ uint32_t fp;
+ uint32_t sp;
+ uint32_t pc;
+} cpu_cxt;
+
+/* This function gets the register values that the schedule() routine
+ * has stored away on the stack to be able to restart a sleeping task.
+ *
+ **/
static void
arm_linux_supply_thread (struct regcache *regcache,
- int regnum, CORE_ADDR addr)
+ int regnum, CORE_ADDR task_struct)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
CORE_ADDR sp = 0;
gdb_byte buf[8];
int i;
+ uint32_t cpsr;
+ uint32_t thread_info_addr;
- gdb_assert (regnum >= -1);
+ DECLARE_FIELD (thread_info, cpu_context);
+ DECLARE_FIELD (task_struct, stack);
- gdb_assert (0);
+ // printf_unfiltered("%s:%d regnum(%d) addr(%p)\n",__func__, __LINE__, regnum, task_struct);
+
+ gdb_assert (regnum >= -1);
+ /*get thread_info address */
+ thread_info_addr = read_unsigned_field (task_struct, task_struct, stack);
+
+ /*get cpu_context as saved by scheduled */
+ read_memory ((CORE_ADDR) thread_info_addr +
+ F_OFFSET (thread_info, cpu_context),
+ (gdb_byte *) & cpu_cxt, sizeof (struct cpu_context_save));
+
+ regcache_raw_supply (regcache, ARM_PC_REGNUM, &cpu_cxt.pc);
+ regcache_raw_supply (regcache, ARM_SP_REGNUM, &cpu_cxt.sp);
+ regcache_raw_supply (regcache, ARM_FP_REGNUM, &cpu_cxt.fp);
+
+ /*general purpose registers */
+ regcache_raw_supply (regcache, 10, &cpu_cxt.sl);
+ regcache_raw_supply (regcache, 9, &cpu_cxt.r9);
+ regcache_raw_supply (regcache, 8, &cpu_cxt.r8);
+ regcache_raw_supply (regcache, 7, &cpu_cxt.r7);
+ regcache_raw_supply (regcache, 6, &cpu_cxt.r6);
+ regcache_raw_supply (regcache, 5, &cpu_cxt.r5);
+ regcache_raw_supply (regcache, 4, &cpu_cxt.r4);
+
+ /* Fake a value for cpsr:T bit. */
+#define IS_THUMB_ADDR(addr) ((addr) & 1)
+ cpsr = IS_THUMB_ADDR(cpu_cxt.pc) ? arm_psr_thumb_bit (target_gdbarch ()) : 0;
+ regcache_raw_supply (regcache, ARM_PS_REGNUM, &cpsr);
+
+ for (i = 0; i < gdbarch_num_regs (target_gdbarch ()); i++)
+ if (REG_VALID != regcache_register_status (regcache, i))
+ /* Mark other registers as unavailable. */
+ regcache_invalidate (regcache, i);
}
static void
@@ -1413,6 +1468,8 @@ arm_linux_collect_thread (const struct regcache *regcache,
gdb_byte buf[8];
int i;
+ printf_unfiltered("%s:%d\n",__func__, __LINE__);
+
gdb_assert (regnum >= -1);
gdb_assert (0);