From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 12546 invoked by alias); 8 Oct 2010 12:55:20 -0000 Received: (qmail 12526 invoked by uid 22791); 8 Oct 2010 12:55:14 -0000 X-SWARE-Spam-Status: No, hits=-1.6 required=5.0 tests=AWL,BAYES_00,MSGID_FROM_MTA_HEADER,TW_BL,TW_OV,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mtagate7.de.ibm.com (HELO mtagate7.de.ibm.com) (195.212.17.167) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 08 Oct 2010 12:55:06 +0000 Received: from d12nrmr1607.megacenter.de.ibm.com (d12nrmr1607.megacenter.de.ibm.com [9.149.167.49]) by mtagate7.de.ibm.com (8.13.1/8.13.1) with ESMTP id o98Ct3x8009927 for ; Fri, 8 Oct 2010 12:55:03 GMT Received: from d12av02.megacenter.de.ibm.com (d12av02.megacenter.de.ibm.com [9.149.165.228]) by d12nrmr1607.megacenter.de.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id o98Ct18C4010232 for ; Fri, 8 Oct 2010 14:55:03 +0200 Received: from d12av02.megacenter.de.ibm.com (loopback [127.0.0.1]) by d12av02.megacenter.de.ibm.com (8.12.11.20060308/8.13.3) with ESMTP id o98Ct0fe011580 for ; Fri, 8 Oct 2010 14:55:01 +0200 Received: from tuxmaker.boeblingen.de.ibm.com (tuxmaker.boeblingen.de.ibm.com [9.152.85.9]) by d12av02.megacenter.de.ibm.com (8.12.11.20060308/8.12.11) with SMTP id o98CsxQj011545; Fri, 8 Oct 2010 14:54:59 +0200 Message-Id: <201010081254.o98CsxQj011545@d12av02.megacenter.de.ibm.com> Received: by tuxmaker.boeblingen.de.ibm.com (sSMTP sendmail emulation); Fri, 08 Oct 2010 14:54:59 +0200 Subject: [rfa] ARM prologue parsing support for Thumb-2 instructions To: dan@codesourcery.com, matthew.gretton-dann@arm.com, rearnsha@arm.com Date: Fri, 08 Oct 2010 12:55:00 -0000 From: "Ulrich Weigand" Cc: gdb-patches@sourceware.org In-Reply-To: <20100719141029.GI6088@caradoc.them.org> from "Daniel Jacobowitz" at Jul 19, 2010 10:10:30 AM MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-10/txt/msg00132.txt.bz2 Daniel Jacobowitz wrote: > On Mon, Jul 19, 2010 at 12:59:05PM +0200, Ulrich Weigand wrote: > > > Trunk thumb_analyze_prologue does have support for Thumb-2. > > > > Well, all the support for Thumb-2 I can see is in this block: > > > > else if ((insn & 0xe000) == 0xe000 && cache == NULL) > > { > > /* Only recognize 32-bit instructions for prologue skipping. */ > > > > which, as the comment says, is active *only* if this routine is > > called from arm_skip_prologue (with cache == NULL), but not if the > > routine is called from arm_scan_prologue (with cache != NULL), > > which is what is used during unwinding. > > IIRC, it would not be hard to fill in the missing pieces; I just > didn't need them at the time, and could not easily test them. So > rather than risk them being wrong, I left them for later. I've finally gotten around to finish my implementation of this. The main part of this patch is support in thumb_analyze_prologue for various Thumb-2 instructions. In addition, the patch improves support for optimized code by skipping unrecognized instructions instead of aborting the prologue scan, and by not relying on line number data to determine an upper bound for the end of the prologue. (Both these changes are already in place for the ARM decoder; the patch simply brings them over to the Thumb decode as well.) Finally, the patch adds support for a couple of more function calls that may happen within the prologue: __tls_get_addr and __aeabi_read_tp. Tested on armv7l-linux-gnueabi with no regressions. In addition, I've run a test with the following line disabled: dwarf2_append_unwinders (gdbarch); This causes GDB to ignore DWARF CFI even if present, and always fall back to prologue parsing. With GDB before this patch, that test introduces a large number of extra regressions. *With* this patch, nearly all of those regressions are fixed again. The following failures still remain: FAIL: gdb.base/call-signal-resume.exp: backtrace FAIL: gdb.base/call-signal-resume.exp: dummy stack frame number FAIL: gdb.base/call-signal-resume.exp: continue to breakpoint at stop_two FAIL: gdb.base/call-signal-resume.exp: continue to receipt of signal FAIL: gdb.base/call-signal-resume.exp: continue to breakpoint at handle_signal FAIL: gdb.base/call-signal-resume.exp: continue to program exit FAIL: gdb.base/foll-vfork.exp: (timeout) vfork parent follow, finish after tcatch vfork FAIL: gdb.base/foll-vfork.exp: vfork child follow, finish after tcatch vfork FAIL: gdb.threads/attach-stopped.exp: threaded: attach2 to stopped bt FAIL: gdb.threads/attachstop-mt.exp: attach0 to sleeping bt FAIL: gdb.threads/attachstop-mt.exp: attach3 to stopped bt FAIL: gdb.threads/attachstop-mt.exp: attach4 to stopped bt FAIL: gdb.threads/pthreads.exp: check backtrace from thread 1 FAIL: gdb.threads/pthreads.exp: check backtrace from thread 2 FAIL: gdb.threads/pthreads.exp: apply backtrace command to all three threads All these are caused by failures to parse the prologues of hand-written *ARM* assembler routines in glibc (system call handlers like nanosleep). These simply deviate too far from the usual rules (e.g. by intermixing stack saving/restoring of registers with conditional branches, or by temporarily saving registers into other registers instead of the stack) for the ARM prologue parser to be able to handle them. It seems to me that there is not much sense in attempting to support even this type of code. I guess we should strongly recommend to have (at least) glibc debuginfo files installed if you want to debug. OK for mainline? Bye, Ulrich ChangeLog: * arm-tdep.c (thumb_expand_immediate): New function. (thumb_instruction_changes_pc): Likewise. (thumb2_instruction_changes_pc): Likewise. (thumb_analyze_prologue): Handle 32-bit Thumb instructions during prologue parsing. Improved support for optimized code. (thumb_scan_prologue): Do not reply on line-number information, use same heuristics as arm_scan_prologue insead. (skip_prologue_function): Accept functions "__tls_get_addr" and "__aeabi_read_tp". Index: gdb/arm-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/arm-tdep.c,v retrieving revision 1.307 diff -u -p -r1.307 arm-tdep.c --- gdb/arm-tdep.c 30 Aug 2010 15:26:28 -0000 1.307 +++ gdb/arm-tdep.c 7 Oct 2010 22:58:27 -0000 @@ -476,6 +476,12 @@ skip_prologue_function (CORE_ADDR pc) if (strncmp (name, "__aeabi_d2f", strlen ("__aeabi_d2f")) == 0) return 1; + /* Internal functions related to thread-local storage. */ + if (strncmp (name, "__tls_get_addr", strlen ("__tls_get_addr")) == 0) + return 1; + if (strncmp (name, "__aeabi_read_tp", strlen ("__aeabi_read_tp")) == 0) + return 1; + return 0; } @@ -488,6 +494,149 @@ skip_prologue_function (CORE_ADDR pc) #define BranchDest(addr,instr) \ ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2))) +/* Decode immediate value; implements ThumbExpandImmediate pseudo-op. */ + +static unsigned int +thumb_expand_immediate (unsigned int imm) +{ + unsigned int count = imm >> 7; + + if (count < 8) + switch (count / 2) + { + case 0: + return imm & 0xff; + case 1: + return (imm & 0xff) | ((imm & 0xff) << 16); + case 2: + return ((imm & 0xff) << 8) | ((imm & 0xff) << 24); + case 3: + return (imm & 0xff) | ((imm & 0xff) << 8) + | ((imm & 0xff) << 16) | ((imm & 0xff) << 24); + } + + return (0x80 | (imm & 0x7f)) << (32 - count); +} + +/* Return 1 if the 16-bit Thumb instruction INST might change + control flow, 0 otherwise. */ + +static int +thumb_instruction_changes_pc (unsigned short inst) +{ + if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */ + return 1; + + if ((inst & 0xf000) == 0xd000) /* conditional branch */ + return 1; + + if ((inst & 0xf800) == 0xe000) /* unconditional branch */ + return 1; + + if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */ + return 1; + + if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */ + return 1; + + return 0; +} + +/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2 + might change control flow, 0 otherwise. */ + +static int +thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2) +{ + if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) + { + /* Branches and miscellaneous control instructions. */ + + if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000) + { + /* B, BL, BLX. */ + return 1; + } + else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00) + { + /* SUBS PC, LR, #imm8. */ + return 1; + } + else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380) + { + /* Conditional branch. */ + return 1; + } + + return 0; + } + + if ((inst1 & 0xfe50) == 0xe810) + { + /* Load multiple or RFE. */ + + if (bit (inst1, 7) && !bit (inst1, 8)) + { + /* LDMIA or POP */ + if (bit (inst2, 15)) + return 1; + } + else if (!bit (inst1, 7) && bit (inst1, 8)) + { + /* LDMDB */ + if (bit (inst2, 15)) + return 1; + } + else if (bit (inst1, 7) && bit (inst1, 8)) + { + /* RFEIA */ + return 1; + } + else if (!bit (inst1, 7) && !bit (inst1, 8)) + { + /* RFEDB */ + return 1; + } + + return 0; + } + + if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00) + { + /* MOV PC or MOVS PC. */ + return 1; + } + + if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000) + { + /* LDR PC. */ + if (bits (inst1, 0, 3) == 15) + return 1; + if (bit (inst1, 7)) + return 1; + if (bit (inst2, 11)) + return 1; + if ((inst2 & 0x0fc0) == 0x0000) + return 1; + + return 0; + } + + if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000) + { + /* TBB. */ + return 1; + } + + if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010) + { + /* TBH. */ + return 1; + } + + return 0; +} + /* Analyze a Thumb prologue, looking for a recognizable stack frame and frame pointer. Scan until we encounter a store that could clobber the stack frame unexpectedly, or an unknown instruction. @@ -506,6 +655,7 @@ thumb_analyze_prologue (struct gdbarch * struct pv_area *stack; struct cleanup *back_to; CORE_ADDR offset; + CORE_ADDR unrecognized_pc = 0; for (i = 0; i < 16; i++) regs[i] = pv_register (i, 0); @@ -643,9 +793,8 @@ thumb_analyze_prologue (struct gdbarch * constant = read_memory_unsigned_integer (loc, 4, byte_order); regs[bits (insn, 8, 10)] = pv_constant (constant); } - else if ((insn & 0xe000) == 0xe000 && cache == NULL) + else if ((insn & 0xe000) == 0xe000) { - /* Only recognize 32-bit instructions for prologue skipping. */ unsigned short inst2; inst2 = read_memory_unsigned_integer (start + 2, 2, @@ -675,59 +824,257 @@ thumb_analyze_prologue (struct gdbarch * if (!skip_prologue_function (nextpc)) break; } - else if ((insn & 0xfe50) == 0xe800 /* stm{db,ia} Rn[!], { registers } */ + + else if ((insn & 0xffd0) == 0xe900 /* stmdb Rn{!}, { registers } */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + pv_t addr = regs[bits (insn, 0, 3)]; + int regno; + + if (pv_area_store_would_trash (stack, addr)) + break; + + /* Calculate offsets of saved registers. */ + for (regno = ARM_LR_REGNUM; regno >= 0; regno--) + if (inst2 & (1 << regno)) + { + addr = pv_add_constant (addr, -4); + pv_area_store (stack, addr, 4, regs[regno]); + } + + if (insn & 0x0020) + regs[bits (insn, 0, 3)] = addr; + } + + else if ((insn & 0xff50) == 0xe940 /* strd Rt, Rt2, [Rn, #+/-imm]{!} */ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + int regno1 = bits (inst2, 12, 15); + int regno2 = bits (inst2, 8, 11); + pv_t addr = regs[bits (insn, 0, 3)]; + + offset = inst2 & 0xff; + if (insn & 0x0080) + addr = pv_add_constant (addr, offset); + else + addr = pv_add_constant (addr, -offset); + + if (pv_area_store_would_trash (stack, addr)) + break; + + pv_area_store (stack, addr, 4, regs[regno1]); + pv_area_store (stack, pv_add_constant (addr, 4), + 4, regs[regno2]); + + if (insn & 0x0020) + regs[bits (insn, 0, 3)] = addr; + } + + else if ((insn & 0xfff0) == 0xf8c0 /* str Rt,[Rn,+/-#imm]{!} */ + && (inst2 & 0x0c00) == 0x0c00 + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + int regno = bits (inst2, 12, 15); + pv_t addr = regs[bits (insn, 0, 3)]; + + offset = inst2 & 0xff; + if (inst2 & 0x0200) + addr = pv_add_constant (addr, offset); + else + addr = pv_add_constant (addr, -offset); + + if (pv_area_store_would_trash (stack, addr)) + break; + + pv_area_store (stack, addr, 4, regs[regno]); + + if (inst2 & 0x0100) + regs[bits (insn, 0, 3)] = addr; + } + + else if ((insn & 0xfff0) == 0xf8c0 /* str.w Rt,[Rn,#imm] */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + int regno = bits (inst2, 12, 15); + pv_t addr; + + offset = inst2 & 0xfff; + addr = pv_add_constant (regs[bits (insn, 0, 3)], offset); + + if (pv_area_store_would_trash (stack, addr)) + break; + + pv_area_store (stack, addr, 4, regs[regno]); + } + + else if ((insn & 0xffd0) == 0xf880 /* str{bh}.w Rt,[Rn,#imm] */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Ignore stores of argument registers to the stack. */ ; - else if ((insn & 0xfe50) == 0xe840 /* strd Rt, Rt2, [Rn, #imm] */ + + else if ((insn & 0xffd0) == 0xf800 /* str{bh} Rt,[Rn,#+/-imm] */ + && (inst2 & 0x0d00) == 0x0c00 && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Ignore stores of argument registers to the stack. */ ; + else if ((insn & 0xffd0) == 0xe890 /* ldmia Rn[!], { registers } */ - && (inst2 & 0x8000) == 0x0000 - && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + && (inst2 & 0x8000) == 0x0000 + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Ignore block loads from the stack, potentially copying + parameters from memory. */ ; - else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */ - && (inst2 & 0x8000) == 0x0000) - /* Since we only recognize this for prologue skipping, do not bother - to compute the constant. */ - regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)]; - else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm12 */ - && (inst2 & 0x8000) == 0x0000) - /* Since we only recognize this for prologue skipping, do not bother - to compute the constant. */ - regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)]; - else if ((insn & 0xfbf0) == 0xf2a0 /* sub.w Rd, Rn, #imm8 */ - && (inst2 & 0x8000) == 0x0000) - /* Since we only recognize this for prologue skipping, do not bother - to compute the constant. */ - regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)]; - else if ((insn & 0xff50) == 0xf850 /* ldr.w Rd, [Rn, #imm]{!} */ + + else if ((insn & 0xffb0) == 0xe950 /* ldrd Rt, Rt2, [Rn, #+/-imm] */ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Similarly ignore dual loads from the stack. */ ; - else if ((insn & 0xff50) == 0xe950 /* ldrd Rt, Rt2, [Rn, #imm]{!} */ + + else if ((insn & 0xfff0) == 0xf850 /* ldr Rt,[Rn,#+/-imm] */ + && (inst2 & 0x0d00) == 0x0c00 && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Similarly ignore single loads from the stack. */ ; - else if ((insn & 0xff50) == 0xf800 /* strb.w or strh.w */ + + else if ((insn & 0xfff0) == 0xf8d0 /* ldr.w Rt,[Rn,#imm] */ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Similarly ignore single loads from the stack. */ ; - else + + else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], + thumb_expand_immediate (imm)); + } + + else if ((insn & 0xfbf0) == 0xf200 /* addw Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], imm); + } + + else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], + - (CORE_ADDR) thumb_expand_immediate (imm)); + } + + else if ((insn & 0xfbf0) == 0xf2a0 /* subw Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], - (CORE_ADDR) imm); + } + + else if ((insn & 0xfbff) == 0xf04f) /* mov.w Rd, #const */ + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_constant (thumb_expand_immediate (imm)); + } + + else if ((insn & 0xfbf0) == 0xf240) /* movw Rd, #const */ + { + unsigned int imm = ((bits (insn, 0, 3) << 12) + | (bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] = pv_constant (imm); + } + + else if (insn == 0xea5f /* mov.w Rd,Rm */ + && (inst2 & 0xf0f0) == 0) + { + int dst_reg = (inst2 & 0x0f00) >> 8; + int src_reg = inst2 & 0xf; + regs[dst_reg] = regs[src_reg]; + } + + else if ((insn & 0xff7f) == 0xf85f) /* ldr.w Rt,