From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 14065 invoked by alias); 5 May 2011 15:10:38 -0000 Received: (qmail 13919 invoked by uid 22791); 5 May 2011 15:10:34 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL,BAYES_00,TW_XF X-Spam-Check-By: sourceware.org Received: from mel.act-europe.fr (HELO mel.act-europe.fr) (194.98.77.210) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 05 May 2011 15:10:19 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id 0810DCB01D6; Thu, 5 May 2011 17:10:18 +0200 (CEST) Received: from mel.act-europe.fr ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6e2liT7SJrpg; Thu, 5 May 2011 17:10:14 +0200 (CEST) Received: from province.act-europe.fr (province.act-europe.fr [10.10.0.214]) by mel.act-europe.fr (Postfix) with ESMTP id 6FF31CB0244; Thu, 5 May 2011 17:10:14 +0200 (CEST) Received: by province.act-europe.fr (Postfix, from userid 560) id 6890F164812; Thu, 5 May 2011 17:10:14 +0200 (CEST) Date: Thu, 05 May 2011 15:10:00 -0000 From: Jerome Guitton To: Mark Kettenis Cc: gdb-patches@sourceware.org Subject: Re: [RFA] Support for x86 on-stack trampolines Message-ID: <20110505151014.GD72917@adacore.com> References: <1304468424-2060-1-git-send-email-guitton@adacore.com> <201105041020.p44AKGt2025840@glazunov.sibelius.xs4all.nl> <20110504151723.GB64873@adacore.com> <201105041530.p44FUMhC015899@glazunov.sibelius.xs4all.nl> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <201105041530.p44FUMhC015899@glazunov.sibelius.xs4all.nl> User-Agent: Mutt/1.5.17 (2007-11-01) 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-05/txt/msg00132.txt.bz2 Mark Kettenis (mark.kettenis@xs4all.nl): > Ah, it looks like I created some confusement. It is i386_find_insn() > as a name that I object to; simply name that funcion i386_match_insn() > and give it the old comment for i386_match_insn(), and I'm happy. > That way you don't have to adjust any of its callers. > > i386_match_pattern() is fine as the name for the broken out code that > matches only a single pattern. Ah, OK, I was confused indeed. Patch updated! OK to apply? gdb/ChangeLog: * i386-tdep.c (i386_in_stack_tramp_p, i386_stack_tramp_frame_sniffer): New functions. (i386_stack_tramp_frame_unwind): New static global. (i386_match_pattern): New function, extracted from i386_match_insn. (i386_match_insn): Use i386_match_pattern. (i386_match_insn_block): New function. (i386_tramp_chain_in_reg_insns) (i386_tramp_chain_on_stack_insns): New static variables. (i386_gdbarch_init): Add i386_stack_tramp_frame_unwind to list of unwinders. diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 5f4089b..b2b7412 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -1126,47 +1126,93 @@ struct i386_insn gdb_byte mask[I386_MAX_MATCHED_INSN_LEN]; }; -/* Search for the instruction at PC in the list SKIP_INSNS. Return - the first instruction description that matches. Otherwise, return - NULL. */ +/* Return whether instruction at PC matches PATTERN. */ -static struct i386_insn * -i386_match_insn (CORE_ADDR pc, struct i386_insn *skip_insns) +static int +i386_match_pattern (CORE_ADDR pc, struct i386_insn pattern) { - struct i386_insn *insn; gdb_byte op; if (target_read_memory (pc, &op, 1)) - return NULL; + return 0; - for (insn = skip_insns; insn->len > 0; insn++) + if ((op & pattern.mask[0]) == pattern.insn[0]) { - if ((op & insn->mask[0]) == insn->insn[0]) - { - gdb_byte buf[I386_MAX_MATCHED_INSN_LEN - 1]; - int insn_matched = 1; - size_t i; - - gdb_assert (insn->len > 1); - gdb_assert (insn->len <= I386_MAX_MATCHED_INSN_LEN); + gdb_byte buf[I386_MAX_MATCHED_INSN_LEN - 1]; + int insn_matched = 1; + size_t i; - if (target_read_memory (pc + 1, buf, insn->len - 1)) - return NULL; + gdb_assert (pattern.len > 1); + gdb_assert (pattern.len <= I386_MAX_MATCHED_INSN_LEN); - for (i = 1; i < insn->len; i++) - { - if ((buf[i - 1] & insn->mask[i]) != insn->insn[i]) - insn_matched = 0; - } + if (target_read_memory (pc + 1, buf, pattern.len - 1)) + return 0; - if (insn_matched) - return insn; + for (i = 1; i < pattern.len; i++) + { + if ((buf[i - 1] & pattern.mask[i]) != pattern.insn[i]) + insn_matched = 0; } + return insn_matched; + } + return 0; +} + +/* Search for the instruction at PC in the list INSN_PATTERNS. Return + the first instruction description that matches. Otherwise, return + NULL. */ + +static struct i386_insn * +i386_match_insn (CORE_ADDR pc, struct i386_insn *insn_patterns) +{ + struct i386_insn *pattern; + + for (pattern = insn_patterns; pattern->len > 0; pattern++) + { + if (i386_match_pattern (pc, *pattern)) + return pattern; } return NULL; } +/* Return whether PC points inside a sequence of instructions that + matches INSN_PATTERNS. */ + +static int +i386_match_insn_block (CORE_ADDR pc, struct i386_insn *insn_patterns) +{ + CORE_ADDR current_pc; + int ix, i; + gdb_byte op; + struct i386_insn *insn; + + insn = i386_match_insn (pc, insn_patterns); + if (insn == NULL) + return 0; + + current_pc = pc - insn->len; + ix = insn - insn_patterns; + for (i = ix - 1; i >= 0; i--) + { + if (!i386_match_pattern (current_pc, insn_patterns[i])) + return 0; + + current_pc -= insn_patterns[i].len; + } + + current_pc = pc + insn->len; + for (insn = insn_patterns + ix + 1; insn->len > 0; insn++) + { + if (!i386_match_pattern (current_pc, *insn)) + return 0; + + current_pc += insn->len; + } + + return 1; +} + /* Some special instructions that might be migrated by GCC into the part of the prologue that sets up the new stack frame. Because the stack frame hasn't been setup yet, no registers have been saved @@ -1938,6 +1984,88 @@ static const struct frame_unwind i386_epilogue_frame_unwind = }; +/* Stack-based trampolines. */ + +/* These trampolines are used on cross x86 targets, when taking the + address of a nested function. When executing these trampolines, + no stack frame is set up, so we are in a similar situation as in + epilogues and i386_epilogue_frame_this_id can be re-used. */ + +/* Static chain passed in register. */ + +struct i386_insn i386_tramp_chain_in_reg_insns[] = +{ + /* `movl imm32, %eax' and `movl imm32, %ecx' */ + { 5, { 0xb8 }, { 0xfe } }, + + /* `jmp imm32' */ + { 5, { 0xe9 }, { 0xff } }, + + {0} +}; + +/* Static chain passed on stack (when regparm=3). */ + +struct i386_insn i386_tramp_chain_on_stack_insns[] = +{ + /* `push imm32' */ + { 5, { 0x68 }, { 0xff } }, + + /* `jmp imm32' */ + { 5, { 0xe9 }, { 0xff } }, + + {0} +}; + +/* Return whether PC points inside a stack trampoline. */ + +static int +i386_in_stack_tramp_p (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + gdb_byte insn; + char *name; + + /* A stack trampoline is detected if no name is associated + to the current pc and if it points inside a trampoline + sequence. */ + + find_pc_partial_function (pc, &name, NULL, NULL); + if (name) + return 0; + + if (target_read_memory (pc, &insn, 1)) + return 0; + + if (!i386_match_insn_block (pc, i386_tramp_chain_in_reg_insns) + && !i386_match_insn_block (pc, i386_tramp_chain_on_stack_insns)) + return 0; + + return 1; +} + +static int +i386_stack_tramp_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_prologue_cache) +{ + if (frame_relative_level (this_frame) == 0) + return i386_in_stack_tramp_p (get_frame_arch (this_frame), + get_frame_pc (this_frame)); + else + return 0; +} + +static const struct frame_unwind i386_stack_tramp_frame_unwind = +{ + NORMAL_FRAME, + i386_epilogue_frame_unwind_stop_reason, + i386_epilogue_frame_this_id, + i386_frame_prev_register, + NULL, + i386_stack_tramp_frame_sniffer +}; + + /* Signal trampolines. */ static struct i386_frame_cache * @@ -7295,6 +7423,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->mm0_regnum = -1; /* Hook in the legacy prologue-based unwinders last (fallback). */ + frame_unwind_append_unwinder (gdbarch, &i386_stack_tramp_frame_unwind); frame_unwind_append_unwinder (gdbarch, &i386_sigtramp_frame_unwind); frame_unwind_append_unwinder (gdbarch, &i386_frame_unwind);