Fix KGDB backtrace on ARM

Modify trapframe decoding to properly analyze trapframe.

Provide method for fixup_pc. It happens, that in some kernel
functions, the GDB stack frame decoder cannot determine both
func name and frame size. This is because these functions
either contain invalid instruction, or their format does
not match standard schema. Detect that scenarios and move
PC accordingly to jump into known function schema, which
GDB is able to parse.

Obtained from:         Semihalf
Sponsored by:          Juniper Networks
Reviewed by:           kib, zbb
Differential Revision: https://reviews.freebsd.org/D5976
This commit is contained in:
Wojciech Macek 2016-04-20 17:58:13 +00:00
parent fb9c3478a1
commit d4015ddc57
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=298358
6 changed files with 83 additions and 19 deletions

View file

@ -678,6 +678,9 @@ arm_scan_prologue (struct frame_info *next_frame, struct arm_prologue_cache *cac
cache->framesize = 0;
cache->frameoffset = 0;
if (frame_tdep_pc_fixup)
frame_tdep_pc_fixup(&prev_pc);
/* Check for Thumb prologue. */
if (arm_pc_is_thumb (prev_pc))
{
@ -914,7 +917,6 @@ arm_make_prologue_cache (struct frame_info *next_frame)
cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
arm_scan_prologue (next_frame, cache);
unwound_fp = frame_unwind_register_unsigned (next_frame, cache->framereg);
if (unwound_fp == 0)
return cache;

View file

@ -136,6 +136,7 @@ static int frame_debug;
static int backtrace_past_main;
static unsigned int backtrace_limit = UINT_MAX;
int (*frame_tdep_pc_fixup)(CORE_ADDR *pc);
void
fprint_frame_id (struct ui_file *file, struct frame_id id)
@ -2010,6 +2011,9 @@ frame_unwind_address_in_block (struct frame_info *next_frame)
/* A draft address. */
CORE_ADDR pc = frame_pc_unwind (next_frame);
if ((frame_tdep_pc_fixup != NULL) && (frame_tdep_pc_fixup(&pc) == 0))
return pc;
/* If THIS frame is not inner most (i.e., NEXT isn't the sentinel),
and NEXT is `normal' (i.e., not a sigtramp, dummy, ....) THIS
frame's PC ends up pointing at the instruction fallowing the

View file

@ -702,4 +702,6 @@ extern struct frame_info *deprecated_frame_xmalloc_with_cleanup (long sizeof_sav
code. */
extern int legacy_frame_p (struct gdbarch *gdbarch);
extern int (*frame_tdep_pc_fixup)(CORE_ADDR *pc);
#endif /* !defined (FRAME_H) */

View file

@ -75,4 +75,7 @@ CORE_ADDR kgdb_parse_1(const char *, int);
#define kgdb_parse(exp) kgdb_parse_1((exp), 0)
#define kgdb_parse_quiet(exp) kgdb_parse_1((exp), 1)
extern int (*arm_tdep_pc_fixup)(CORE_ADDR *pc);
int kgdb_trgt_pc_fixup(CORE_ADDR *pc);
#endif /* _KGDB_H_ */

View file

@ -474,7 +474,9 @@ main(int argc, char *argv[])
add_arg(&args, NULL);
init_ui_hook = kgdb_init;
#if TARGET_CPUARCH == arm
frame_tdep_pc_fixup = kgdb_trgt_pc_fixup;
#endif
kgdb_sniffer_kluge = kgdb_trgt_trapframe_sniffer;
return (gdb_main(&args));

View file

@ -96,6 +96,7 @@ kgdb_trgt_new_objfile(struct objfile *objfile)
struct kgdb_frame_cache {
CORE_ADDR fp;
CORE_ADDR sp;
CORE_ADDR pc;
};
static int kgdb_trgt_frame_offset[26] = {
@ -135,6 +136,7 @@ kgdb_trgt_frame_cache(struct frame_info *next_frame, void **this_cache)
frame_unwind_register(next_frame, ARM_FP_REGNUM, buf);
cache->fp = extract_unsigned_integer(buf,
register_size(current_gdbarch, ARM_FP_REGNUM));
cache->pc = frame_func_unwind(next_frame);
}
return (cache);
}
@ -148,7 +150,7 @@ kgdb_trgt_trapframe_this_id(struct frame_info *next_frame, void **this_cache,
struct kgdb_frame_cache *cache;
cache = kgdb_trgt_frame_cache(next_frame, this_cache);
*this_id = frame_id_build(cache->fp, 0);
*this_id = frame_id_build(cache->sp, cache->pc);
}
static void
@ -159,7 +161,7 @@ kgdb_trgt_trapframe_prev_register(struct frame_info *next_frame,
char dummy_valuep[MAX_REGISTER_SIZE];
struct kgdb_frame_cache *cache;
int ofs, regsz;
int is_undefined = 0;
CORE_ADDR sp;
regsz = register_size(current_gdbarch, regnum);
@ -177,24 +179,12 @@ kgdb_trgt_trapframe_prev_register(struct frame_info *next_frame,
return;
cache = kgdb_trgt_frame_cache(next_frame, this_cache);
sp = cache->sp;
if (is_undef && (regnum == ARM_SP_REGNUM || regnum == ARM_PC_REGNUM)) {
*addrp = cache->sp + offsetof(struct trapframe, tf_spsr);
target_read_memory(*addrp, valuep, regsz);
is_undefined = 1;
ofs = kgdb_trgt_frame_offset[ARM_SP_REGNUM];
}
*addrp = cache->sp + ofs;
ofs = kgdb_trgt_frame_offset[regnum];
*addrp = sp + ofs;
*lvalp = lval_memory;
target_read_memory(*addrp, valuep, regsz);
if (is_undefined) {
*addrp = *(unsigned int *)valuep + (regnum == ARM_SP_REGNUM ?
0 : 8);
target_read_memory(*addrp, valuep, regsz);
}
}
static const struct frame_unwind kgdb_trgt_trapframe_unwind = {
@ -233,3 +223,64 @@ kgdb_trgt_trapframe_sniffer(struct frame_info *next_frame)
#endif
return (NULL);
}
/*
* This function ensures, that the PC is inside the
* function section which is understood by GDB.
*
* Return 0 when fixup is necessary, -1 otherwise.
*/
int
kgdb_trgt_pc_fixup(CORE_ADDR *pc)
{
#ifndef CROSS_DEBUGGER
struct minimal_symbol *msymbol;
int valpc;
/*
* exception_exit and swi_exit are special. These functions
* are artificially injected into the stack to be executed
* as the last entry in calling chain when all functions exit.
* Treat them differently.
*/
msymbol = lookup_minimal_symbol_by_pc(*pc);
if (msymbol != NULL) {
if (strcmp(DEPRECATED_SYMBOL_NAME(msymbol), "exception_exit") == 0)
return (0);
if (strcmp(DEPRECATED_SYMBOL_NAME(msymbol), "swi_exit") == 0)
return (0);
}
/*
* kdb_enter contains an invalid instruction which is supposed
* to generate a trap. BFD does not understand it and treats
* this part of function as a separate function. Move PC
* two instruction earlier to be inside kdb_enter section.
*/
target_read_memory(*pc - 4, (char*)&valpc, 4);
if (valpc == 0xe7ffffff) {
*pc = *pc - 8;
return (0);
}
/*
* When the panic/vpanic is the last (noreturn) function,
* the bottom of the calling function looks as below.
* mov lr, pc
* b panic
* Normally, GDB is not able to detect function boundaries,
* so move the PC two instruction earlier where it can deal
* with it.
* Match this pair of instructions: mov lr, pc followed with
* non-linked branch.
*/
if ((valpc & 0xff000000) == 0xea000000) {
target_read_memory(*pc - 8, (char*)&valpc, 4);
if (valpc == 0xe1a0e00f) {
*pc -= 8;
return (0);
}
}
#endif
return (-1);
}