From c9e4a8a1c9b60ce957cab14f8725b76dabf7e998 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 15 Jun 2016 14:59:06 +0100 Subject: 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 --- gdb/arm-linux-tdep.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file 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); -- cgit v1.2.3