From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 11381 invoked by alias); 14 Jun 2011 11:49:48 -0000 Received: (qmail 11372 invoked by uid 22791); 14 Jun 2011 11:49:46 -0000 X-SWARE-Spam-Status: No, hits=-1.9 required=5.0 tests=AWL,BAYES_00,TW_EB,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 14 Jun 2011 11:49:32 +0000 Received: (qmail 24812 invoked from network); 14 Jun 2011 11:49:31 -0000 Received: from unknown (HELO scottsdale.localnet) (pedro@127.0.0.2) by mail.codesourcery.com with ESMTPA; 14 Jun 2011 11:49:31 -0000 From: Pedro Alves To: gdb-patches@sourceware.org Subject: Re: [RFC] i386 PLT stub unwinder Date: Tue, 14 Jun 2011 11:49:00 -0000 User-Agent: KMail/1.13.6 (Linux/2.6.38-8-generic; KDE/4.6.2; x86_64; ; ) Cc: Mark Kettenis References: <201106131555.p5DFtucc019690@glazunov.sibelius.xs4all.nl> In-Reply-To: <201106131555.p5DFtucc019690@glazunov.sibelius.xs4all.nl> MIME-Version: 1.0 Content-Type: Text/Plain; charset="iso-8859-15" Content-Transfer-Encoding: 7bit Message-Id: <201106141249.28010.pedro@codesourcery.com> X-IsSubscribed: yes 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: 2011-06/txt/msg00188.txt.bz2 On Monday 13 June 2011 16:55:56, Mark Kettenis wrote: > Jan's mail about DWARF CFI for PLT stubs prompted me to write an > undinder for the PLT stubs as defined by the i386 ABI. With this > change I can step through the PLT stubs and always have a proper > backtrace. > > The reason that this is an RFC, is that I'm a little bit confused > about the TRY_CATCH stuff that was introduced in some of the i386 > unwinders by Pedro. It isn't entirely clear to me when that is > needed. Unwinding the PC should always succeed at this point, since > the sniffers already rely on that. And unwinding the stack pointer > needs to be working as well, otherwise unwinding the PC will never > have worked. So I don't think I need to worry about unavailable > registers here. Not correct. If the PC was not available, the sniffer wouldn't be able to detect the program is sitting in a plt, so in that case you indeed don't have to worry. But, all the sniffer needs to be able to decide the plt unwinder is the best unwinder is to have the PC available and being able to read some instructions off of that address. If the SP is unavailable, and so you can't compute the frame base, the frame needs to be marked un-unwindable. E.g., with your patch as is: $ gdb ~/gdb/tests/loop32 ... (gdb) tar remote :9999 Remote debugging using :9999 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 0xf7fe0850 in ?? () from /lib/ld-linux.so.2 (gdb) b main Breakpoint 1 at 0x804850e: file loop.c, line 84. (gdb) c Continuing. Breakpoint 1, main () at loop.c:84 84 long i = 0; (gdb) si ... (gdb) 0x080483c0 in pthread_create@plt () (gdb) disassemble Dump of assembler code for function pthread_create@plt: => 0x080483c0 <+0>: jmp *0x804a00c 0x080483c6 <+6>: push $0x18 0x080483cb <+11>: jmp 0x8048380 End of assembler dump. (gdb) trace *0x080483c6 Tracepoint 2 at 0x80483c6 (gdb) tstart (gdb) n Single stepping until exit from function pthread_create@plt, which has no line number information. 0x08048380 in ?? () (gdb) tstatus Trace is running on the target. Collected 1 trace frames. Trace buffer has 5242874 bytes of 5242880 bytes free (0% full). Trace will stop if GDB disconnects. Not looking at any trace frame. (gdb) tstop (gdb) tfind Found trace frame 0, tracepoint 2 Register 4 is not available ^^^^^^^^^^^^^^^^^^^^^^^^^^^ That is an error being thrown thrown from: (top-gdb) bt #0 throw_error (error=..., fmt=0x7fffffffee15 "") at ../../src/gdb/exceptions.c:420 #1 0x0000000000466705 in frame_unwind_register (frame=0xf20b30, regnum=4, buf=0x7fffffffd600 "") at ../../src/gdb/frame.c:919 #2 0x0000000000466b4d in frame_unwind_register_unsigned (frame=0xf20b30, regnum=4) at ../../src/gdb/frame.c:1025 #3 0x0000000000466b9e in get_frame_register_unsigned (frame=0xf20bf0, regnum=4) at ../../src/gdb/frame.c:1032 #4 0x000000000048325a in i386_plt_stub_frame_cache (this_frame=0xf20bf0, this_cache=0xf20c08) at ../../src/gdb/i386-tdep.c:2208 #5 0x00000000004832d6 in i386_plt_stub_frame_this_id (this_frame=0xf20bf0, this_cache=0xf20c08, this_id=0xf20c50) at ../../src/gdb/i386-tdep.c:2221 #6 0x00000000004653c4 in get_frame_id (fi=0xf20bf0) at ../../src/gdb/frame.c:339 #7 0x0000000000467ed1 in get_prev_frame_1 (this_frame=0xf20bf0) at ../../src/gdb/frame.c:1628 #8 0x00000000004687b7 in get_prev_frame (this_frame=0xf20bf0) at ../../src/gdb/frame.c:1950 #9 0x000000000059c991 in backtrace_command_1 (count_exp=0x0, show_locals=0, from_tty=1) at ../../src/gdb/stack.c:1374 #10 0x000000000059ca57 in backtrace_command_stub (data=0x7fffffffd9d0) at ../../src/gdb/stack.c:1421 #11 0x00000000005a2057 in catch_errors (func=0x59ca24 , func_args=0x7fffffffd9d0, errstring=0x809fc8 "", mask=2) at ../../src/gdb/exceptions.c:506 #12 0x000000000059cc93 in backtrace_command (arg=0x0, from_tty=1) at ../../src/gdb/stack.c:1479 That is: > +static struct i386_frame_cache * > +i386_plt_stub_frame_cache (struct frame_info *this_frame, void **this_cache) > +{ ... > + > + sp = get_frame_register_unsigned (this_frame, I386_ESP_REGNUM); > + cache->base = sp + cache->sp_offset; You should wrap that in a TRY_CATCH for NOT_AVAILABLE_ERROR like the other i386 unwinders, and > + cache->base_p = 1; leave that false if the base wasn't computable, > +static const struct frame_unwind i386_plt_stub_frame_unwind = > +{ > + NORMAL_FRAME, > + default_frame_unwind_stop_reason, and here install a i386_plt_stub_frame_unwind_stop_reason function that returns UNWIND_UNAVAILABLE if cache->base_p is false, just like e.g., i386_epilogue_frame_unwind_stop_reason. Might as well write that in patch form. See below. You can merge it into yours, or have me apply it when yours goes in, as you prefer. Here's what it looks like with the patch applied. Again, inspecting a tracepoint where only PC is available: (gdb) tstatus Trace is running on the target. Collected 1 trace frames. Trace buffer has 5242874 bytes of 5242880 bytes free (0% full). Trace will stop if GDB disconnects. Not looking at any trace frame. (gdb) tstop (gdb) tfind Found trace frame 0, tracepoint 2 #0 0x080483cb in pthread_create@plt () (gdb) bt #0 0x080483cb in pthread_create@plt () Backtrace stopped: Not enough registers or memory available to unwind further (gdb) info registers eax *value not available* ecx *value not available* edx *value not available* ebx *value not available* esp *value not available* ebp *value not available* esi *value not available* edi *value not available* eip 0x80483cb 0x80483cb eflags *value not available* cs *value not available* ss *value not available* ds *value not available* es *value not available* fs *value not available* gs *value not available* (gdb) info frame Stack level 0, frame at 0x8: eip = 0x80483cb in pthread_create@plt; saved eip 0x80483cb Outermost frame: Not enough registers or memory available to unwind further Arglist at unknown address. Locals at unknown address,Register 9 is not available (gdb) up Initial frame selected; you cannot go up. (that "Register 9 is not available" in info frame's output is a common code bug I had missed before) -- Pedro Alves --- gdb/i386-tdep.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) Index: src/gdb/i386-tdep.c =================================================================== --- src.orig/gdb/i386-tdep.c 2011-06-14 12:05:30.000000000 +0100 +++ src/gdb/i386-tdep.c 2011-06-14 12:34:55.466490000 +0100 @@ -2125,6 +2125,7 @@ struct i386_insn i386_pic_plt_stub_insns static struct i386_frame_cache * i386_plt_stub_frame_cache (struct frame_info *this_frame, void **this_cache) { + volatile struct gdb_exception ex; struct i386_frame_cache *cache; struct i386_insn *insn; LONGEST sp_offset = -4; @@ -2205,15 +2206,33 @@ i386_plt_stub_frame_cache (struct frame_ cache->pc = pc; - sp = get_frame_register_unsigned (this_frame, I386_ESP_REGNUM); - cache->base = sp + cache->sp_offset; - cache->saved_sp = cache->base + 8; - cache->saved_regs[I386_EIP_REGNUM] = cache->base + 4; + TRY_CATCH (ex, RETURN_MASK_ERROR) + { + sp = get_frame_register_unsigned (this_frame, I386_ESP_REGNUM); + cache->base = sp + cache->sp_offset; + cache->saved_sp = cache->base + 8; + cache->saved_regs[I386_EIP_REGNUM] = cache->base + 4; + cache->base_p = 1; + } + if (ex.reason < 0 && ex.error != NOT_AVAILABLE_ERROR) + throw_exception (ex); - cache->base_p = 1; return cache; } +static enum unwind_stop_reason +i386_plt_stub_frame_unwind_stop_reason (struct frame_info *this_frame, + void **this_cache) +{ + struct i386_frame_cache *cache = + i386_plt_stub_frame_cache (this_frame, this_cache); + + if (!cache->base_p) + return UNWIND_UNAVAILABLE; + + return UNWIND_NO_REASON; +} + static void i386_plt_stub_frame_this_id (struct frame_info *this_frame, void **this_cache, struct frame_id *this_id) @@ -2256,7 +2275,7 @@ i386_plt_stub_frame_sniffer (const struc static const struct frame_unwind i386_plt_stub_frame_unwind = { NORMAL_FRAME, - default_frame_unwind_stop_reason, + i386_plt_stub_frame_unwind_stop_reason, i386_plt_stub_frame_this_id, i386_plt_stub_frame_prev_register, NULL,