aboutsummaryrefslogtreecommitdiff
path: root/gnu/usr.bin
diff options
context:
space:
mode:
authorWojciech Macek <wma@FreeBSD.org>2016-04-20 17:58:13 +0000
committerWojciech Macek <wma@FreeBSD.org>2016-04-20 17:58:13 +0000
commitd4015ddc57f4920eaf184ab5c5dc246140142c5a (patch)
treebcc9076b944062771b44adf7b9d04d207d8b83bf /gnu/usr.bin
parentfb9c3478a12f677153b17e136b9f35f059c73781 (diff)
downloadsrc-d4015ddc57f4920eaf184ab5c5dc246140142c5a.tar.gz
src-d4015ddc57f4920eaf184ab5c5dc246140142c5a.zip
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
Notes
Notes: svn path=/head/; revision=298358
Diffstat (limited to 'gnu/usr.bin')
-rw-r--r--gnu/usr.bin/gdb/kgdb/kgdb.h3
-rw-r--r--gnu/usr.bin/gdb/kgdb/main.c4
-rw-r--r--gnu/usr.bin/gdb/kgdb/trgt_arm.c85
3 files changed, 74 insertions, 18 deletions
diff --git a/gnu/usr.bin/gdb/kgdb/kgdb.h b/gnu/usr.bin/gdb/kgdb/kgdb.h
index 379861b0cadb..8e5d2baf40b4 100644
--- a/gnu/usr.bin/gdb/kgdb/kgdb.h
+++ b/gnu/usr.bin/gdb/kgdb/kgdb.h
@@ -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_ */
diff --git a/gnu/usr.bin/gdb/kgdb/main.c b/gnu/usr.bin/gdb/kgdb/main.c
index aa062a2dd8c0..ce45c780bf17 100644
--- a/gnu/usr.bin/gdb/kgdb/main.c
+++ b/gnu/usr.bin/gdb/kgdb/main.c
@@ -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));
diff --git a/gnu/usr.bin/gdb/kgdb/trgt_arm.c b/gnu/usr.bin/gdb/kgdb/trgt_arm.c
index d457bd3b43dd..13111bc43469 100644
--- a/gnu/usr.bin/gdb/kgdb/trgt_arm.c
+++ b/gnu/usr.bin/gdb/kgdb/trgt_arm.c
@@ -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);
+}