From owner-svn-src-all@freebsd.org Wed Apr 20 17:58:14 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id BFCC5B16E11; Wed, 20 Apr 2016 17:58:14 +0000 (UTC) (envelope-from wma@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 822CC1294; Wed, 20 Apr 2016 17:58:14 +0000 (UTC) (envelope-from wma@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u3KHwDiL028378; Wed, 20 Apr 2016 17:58:13 GMT (envelope-from wma@FreeBSD.org) Received: (from wma@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u3KHwDNq028372; Wed, 20 Apr 2016 17:58:13 GMT (envelope-from wma@FreeBSD.org) Message-Id: <201604201758.u3KHwDNq028372@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wma set sender to wma@FreeBSD.org using -f From: Wojciech Macek Date: Wed, 20 Apr 2016 17:58:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r298358 - in head: contrib/gdb/gdb gnu/usr.bin/gdb/kgdb X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Apr 2016 17:58:14 -0000 Author: wma Date: Wed Apr 20 17:58:13 2016 New Revision: 298358 URL: https://svnweb.freebsd.org/changeset/base/298358 Log: 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 Modified: head/contrib/gdb/gdb/arm-tdep.c head/contrib/gdb/gdb/frame.c head/contrib/gdb/gdb/frame.h head/gnu/usr.bin/gdb/kgdb/kgdb.h head/gnu/usr.bin/gdb/kgdb/main.c head/gnu/usr.bin/gdb/kgdb/trgt_arm.c Modified: head/contrib/gdb/gdb/arm-tdep.c ============================================================================== --- head/contrib/gdb/gdb/arm-tdep.c Wed Apr 20 17:54:53 2016 (r298357) +++ head/contrib/gdb/gdb/arm-tdep.c Wed Apr 20 17:58:13 2016 (r298358) @@ -678,6 +678,9 @@ arm_scan_prologue (struct frame_info *ne 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_in 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; Modified: head/contrib/gdb/gdb/frame.c ============================================================================== --- head/contrib/gdb/gdb/frame.c Wed Apr 20 17:54:53 2016 (r298357) +++ head/contrib/gdb/gdb/frame.c Wed Apr 20 17:58:13 2016 (r298358) @@ -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 fr /* 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 Modified: head/contrib/gdb/gdb/frame.h ============================================================================== --- head/contrib/gdb/gdb/frame.h Wed Apr 20 17:54:53 2016 (r298357) +++ head/contrib/gdb/gdb/frame.h Wed Apr 20 17:58:13 2016 (r298358) @@ -702,4 +702,6 @@ extern struct frame_info *deprecated_fra code. */ extern int legacy_frame_p (struct gdbarch *gdbarch); +extern int (*frame_tdep_pc_fixup)(CORE_ADDR *pc); + #endif /* !defined (FRAME_H) */ Modified: head/gnu/usr.bin/gdb/kgdb/kgdb.h ============================================================================== --- head/gnu/usr.bin/gdb/kgdb/kgdb.h Wed Apr 20 17:54:53 2016 (r298357) +++ head/gnu/usr.bin/gdb/kgdb/kgdb.h Wed Apr 20 17:58:13 2016 (r298358) @@ -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_ */ Modified: head/gnu/usr.bin/gdb/kgdb/main.c ============================================================================== --- head/gnu/usr.bin/gdb/kgdb/main.c Wed Apr 20 17:54:53 2016 (r298357) +++ head/gnu/usr.bin/gdb/kgdb/main.c Wed Apr 20 17:58:13 2016 (r298358) @@ -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)); Modified: head/gnu/usr.bin/gdb/kgdb/trgt_arm.c ============================================================================== --- head/gnu/usr.bin/gdb/kgdb/trgt_arm.c Wed Apr 20 17:54:53 2016 (r298357) +++ head/gnu/usr.bin/gdb/kgdb/trgt_arm.c Wed Apr 20 17:58:13 2016 (r298358) @@ -96,6 +96,7 @@ kgdb_trgt_new_objfile(struct objfile *ob 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 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 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 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 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 #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); +}