2007-10-13 Pedro Alves * gdbarch.sh (gdbarch_skip___main_call): New. * gdbarch.h, gdbarch.c: Regenerate. * i386-tdep.h (i386_skip___main_call): Declare. * i386-tdep.c (i386_skip_alloca_call, i386_analyze_alloca2) (i386_analyze_alloca): New functions. (i386_analyze_frame_setup): Account for register saves before stack adjustment, for stack adjustment with _alloca and for stack adjustment with andl. Relax the insns requence. (i386___main_skip_insns): New. (i386_skip___main_call): New. (i386_analyze_prologue): Call i386_analyze_alloca. (i386_skip_prologue): Skip calls to __main on targets where gcc emits that call. (i386_frame_cache): If needed, extract %ecx from the stack. (i386_frame_args_address): New. (i386_frame_base): Set i386_frame_args_address as args method. * i386-cygwin-tdep.c (i386_cygwin_init_abi): Register i386_skip___main_call as gdbarch_skip___main_call gdbarch callback. --- gdb/gdbarch.c | 33 ++++ gdb/gdbarch.h | 6 gdb/gdbarch.sh | 1 gdb/i386-cygwin-tdep.c | 2 gdb/i386-tdep.c | 341 ++++++++++++++++++++++++++++++++++++++++++++++--- gdb/i386-tdep.h | 3 6 files changed, 368 insertions(+), 18 deletions(-) Index: src/gdb/i386-tdep.c =================================================================== --- src.orig/gdb/i386-tdep.c 2007-10-13 01:51:08.000000000 +0100 +++ src/gdb/i386-tdep.c 2007-10-13 20:32:54.000000000 +0100 @@ -579,6 +579,185 @@ i386_match_insn (CORE_ADDR pc, struct i3 return NULL; } +/* Check that the code pointed to by PC corresponds to a call to + __chkstk/_alloca and skip it if so. Return PC otherwise. */ + +static CORE_ADDR +i386_skip_alloca_call (CORE_ADDR pc, CORE_ADDR limit) +{ + gdb_byte op; + + /* If we don't have enough code for a call, return now. */ + if (pc + 5 > limit) + return pc; + + read_memory_nobpt (pc, &op, 1); + if (op == 0xe8) + { + gdb_byte buf[4]; + if (target_read_memory (pc + 1, buf, sizeof buf) == 0) + { + CORE_ADDR call_dest = pc + 5 + extract_unsigned_integer (buf, 4); + + struct minimal_symbol *s = + lookup_minimal_symbol_by_pc (call_dest); + if (s != NULL + && SYMBOL_LINKAGE_NAME (s) != NULL + && (strcmp (SYMBOL_LINKAGE_NAME (s), "__chkstk") == 0 + || strcmp (SYMBOL_LINKAGE_NAME (s), "_alloca") == 0)) + pc += 5; + } + } + + return pc; +} + +/* Check that the code pointed to by PC corresponds to a call to + __chkstk/_alloca and skip it if so. Return PC otherwise. */ + +static CORE_ADDR +i386_analyze_alloca2 (CORE_ADDR pc, CORE_ADDR limit, + struct i386_frame_cache *cache) +{ + /* On gcc3 ,and at -O0, the usual sequence in main is given by + setting %eax, storing/retrieving it (this is unoptimized code), + and then an _alloca/__chkstk call, which takes it's parameter in + %eax: + + eg: + mov $0x0,%eax + add $0xf,%eax + add $0xf,%eax + shr $0x4,%eax + shl $0x4,%eax + mov %eax,xxx(%ebp) + mov xxx(%ebp),%eax + call __alloca + + Also, if local variables take more than a page (4k), the stack + adjustment will be done using _alloca, because of the need to + touch the guard page on NT. The sequence will be: + + mov $xxx,%eax + call __alloca + + That's valid for main, and for every other function. At -O1, + long sequence above turns into the second sequence (mov,call). + + On gcc3, at -O2 and above, the `mov $xxx,%eax' is often migrated + into the frame setup, but we don't support that currently. Eg: + + push %ebp + mov $0x10,%eax + mov %esp,%ebp + sub $0x8,%esp + and $0xfffffff0,%esp + call __alloca + call ___main + + */ + + gdb_byte op; + + const gdb_byte insns[] = { + 0xb8, 0x00, 0x00, 0x00, 0x00, /* mov $0x0,%eax */ + 0x83, 0xc0, 0x0f, /* add $0xf,%eax */ + 0x83, 0xc0, 0x0f, /* add $0xf,%eax */ + 0xc1, 0xe8, 0x04, /* shr $0x4,%eax */ + 0xc1, 0xe0, 0x04, /* shl $0x4,%eax */ + }; + gdb_byte buf[sizeof insns]; + + if (target_read_memory (pc, buf, sizeof buf) == 0 + && memcmp (buf, insns, sizeof insns) == 0) + { + CORE_ADDR pc2 = pc + sizeof insns; + CORE_ADDR pos; + + /* mov %eax,xxx(%ebp) */ + read_memory_nobpt (pc2, &op, 1); + if (op == 0x89) + { + read_memory_nobpt (pc2 + 1, &op, 1); + switch (op) + { + case 0x45: + pc2 += 3; + break; + case 0x85: + pc2 += 6; + break; + default: + goto other; + } + } + else + goto other; + + /* mov xxx(%ebp),%eax */ + read_memory_nobpt (pc2, &op, 1); + if (op == 0x8b) + { + read_memory_nobpt (pc2 + 1, &op, 1); + switch (op) + { + case 0x45: + pc2 += 3; + break; + case 0x85: + pc2 += 6; + break; + default: + goto other; + } + } + + pos = i386_skip_alloca_call (pc2, limit); + if (pos != pc2) + { + cache->locals += 0x10; + pc = pos; + + if (limit <= pc) + return limit; + } + } + + other: + /* mov $xxx,%eax */ + read_memory_nobpt (pc, &op, 1); + if (op == 0xb8) + { + CORE_ADDR pos = i386_skip_alloca_call (pc + 5, limit); + if (pos != pc + 5) + { + ULONGEST eax = read_memory_unsigned_integer (pc + 1, 4); + cache->locals += eax; + pc = pos; + + if (limit <= pc) + return limit; + } + } + + return pc; +} + +/* Check that the code pointed to by PC corresponds to a call to + __chkstk/_alloca and skip it if so. Return PC otherwise. */ + +static CORE_ADDR +i386_analyze_alloca (CORE_ADDR pc, CORE_ADDR limit, + struct i386_frame_cache *cache) +{ + CORE_ADDR next_pc; + + while ((next_pc = i386_analyze_alloca2 (pc, limit, cache)) != pc) + pc = next_pc; + + return pc; +} + /* 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 @@ -634,6 +813,10 @@ struct i386_insn i386_frame_setup_skip_i { 0 } }; +static CORE_ADDR +i386_analyze_register_saves (CORE_ADDR pc, CORE_ADDR current_pc, + struct i386_frame_cache *cache); + /* Check whether PC points at a code that sets up a new stack frame. If so, it updates CACHE and returns the address of the first instruction after the sequence that sets up the frame or LIMIT, @@ -713,7 +896,11 @@ i386_analyze_frame_setup (CORE_ADDR pc, if (limit <= pc) return limit; - /* Check for stack adjustment + /* Check for registers saves seen before stack adjustment. Seen + on targets that emit a call to __main. */ + pc = i386_analyze_register_saves (pc, limit, cache); + + /* Check for stack adjustment subl $XXX, %esp @@ -730,7 +917,7 @@ i386_analyze_frame_setup (CORE_ADDR pc, /* `subl' with signed 8-bit immediate (though it wouldn't make sense to be negative). */ cache->locals = read_memory_integer (pc + 2, 1); - return pc + 3; + pc += 3; } else if (op == 0x81) { @@ -741,13 +928,26 @@ i386_analyze_frame_setup (CORE_ADDR pc, /* It is `subl' with a 32-bit immediate. */ cache->locals = read_memory_integer (pc + 2, 4); - return pc + 6; - } - else - { - /* Some instruction other than `subl'. */ - return pc; + pc += 6; } + + /* _alloca call */ + pc = i386_analyze_alloca (pc, limit, cache); + + /* and $-16,%esp */ + { + const gdb_byte insns[] = { + 0x83, 0xe4, 0xf0 /* and $0xfffffff0,%esp */ + }; + gdb_byte buf[sizeof insns]; + + if (target_read_memory (pc, buf, sizeof buf) == 0 + && memcmp (buf, insns, sizeof insns) == 0) + { + cache->locals &= -16L; + return pc + 3; + } + } } else if (op == 0xc8) /* enter */ { @@ -758,6 +958,73 @@ i386_analyze_frame_setup (CORE_ADDR pc, return pc; } +/* Some instructions that might be migrated by GCC into after the + prologue and just before the __main call. */ + +struct i386_insn i386___main_skip_insns[] = +{ + /* `mov %ecx,%ebx' */ + { 2, { 0x89, 0xcb }, { 0xff, 0xff } }, + + /* `mov (%ecx),%esi' */ + { 2, { 0x8b, 0x31 }, { 0xff, 0xff } }, + + /* `mov xxx(%ecx),%ebx' */ + { 3, { 0x8b, 0x59 }, { 0xff, 0xff } }, + + { 0 } +}; + + +/* Check that the code pointed to by PC corresponds to a call to + __main, skip it if so. Return PC otherwise. */ + +CORE_ADDR +i386_skip___main_call (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + gdb_byte op; + int skip = 0; + CORE_ADDR limit = pc + 20; + + /* Check for some instructions that might be migrated by + GCC into before the __main call and skip them. + + Make sure we only skip these instructions if we later see the + `call ___main'. */ + while (pc + skip < limit) + { + struct i386_insn *insn; + insn = i386_match_insn (pc + skip, i386___main_skip_insns); + if (insn == NULL) + break; + + skip += insn->len; + } + + /* If that's all, return now. */ + if (limit <= pc) + return limit; + + read_memory_nobpt (pc + skip, &op, 1); + if (op == 0xe8) + { + gdb_byte buf[4]; + if (target_read_memory (pc + skip + 1, buf, sizeof buf) == 0) + { + CORE_ADDR call_dest = pc + skip + 5 + extract_unsigned_integer (buf, 4); + + struct minimal_symbol *s = + lookup_minimal_symbol_by_pc (call_dest); + if (s != NULL + && SYMBOL_LINKAGE_NAME (s) != NULL + && strcmp (SYMBOL_LINKAGE_NAME (s), "__main") == 0) + pc += skip + 5; + } + } + + return pc; +} + /* Check whether PC points at code that saves registers on the stack. If so, it updates CACHE and returns the address of the first instruction after the register saves or CURRENT_PC, whichever is @@ -802,16 +1069,18 @@ i386_analyze_register_saves (CORE_ADDR p once used in the System V compiler). Local space is allocated just below the saved %ebp by either the - 'enter' instruction, or by "subl $, %esp". 'enter' has a - 16-bit unsigned argument for space to allocate, and the 'addl' - instruction could have either a signed byte, or 32-bit immediate. + 'enter' instruction, or by "subl $, %esp", or by a call to + _alloca (libgcc/Cygwin). 'enter' has a 16-bit unsigned argument + for space to allocate; the 'addl' instruction could have either a + signed byte, or 32-bit immediate; the _alloca call is passed the + stack space to allocate in %eax. Next, the registers used by this function are pushed. With the System V compiler they will always be in the order: %edi, %esi, %ebx (and sometimes a harmless bug causes it to also save but not restore %eax); however, the code below is willing to see the pushes in any order, and will handle up to 8 of them. - + If the setup sequence is at the end of the function, then the next instruction will be a branch back to the start. */ @@ -824,7 +1093,10 @@ i386_analyze_prologue (CORE_ADDR pc, COR pc = i386_skip_probe (pc); pc = i386_analyze_stack_align (pc, current_pc, cache); pc = i386_analyze_frame_setup (pc, current_pc, cache); - return i386_analyze_register_saves (pc, current_pc, cache); + pc = i386_analyze_register_saves (pc, current_pc, cache); + pc = i386_analyze_alloca (pc, current_pc, cache); + + return pc; } /* Return PC of first real instruction. */ @@ -902,6 +1174,17 @@ i386_skip_prologue (CORE_ADDR start_pc) if (i386_follow_jump (start_pc) != start_pc) pc = i386_follow_jump (pc); + /* Skip __main calls in main. */ + if (gdbarch_skip___main_call_p (current_gdbarch)) + { + struct minimal_symbol *mainf = lookup_minimal_symbol_by_pc (start_pc); + + if (mainf != NULL + && SYMBOL_LINKAGE_NAME (mainf) + && strcmp (SYMBOL_LINKAGE_NAME (mainf), "main") == 0) + pc = gdbarch_skip___main_call (current_gdbarch, pc); + } + return pc; } @@ -956,8 +1239,19 @@ i386_frame_cache (struct frame_info *nex if (cache->stack_align) { /* Saved stack pointer has been saved in %ecx. */ - frame_unwind_register (next_frame, I386_ECX_REGNUM, buf); - cache->saved_sp = extract_unsigned_integer(buf, 4); + + /* If we are analysing a `main', and this target emits __main + calls, %ecx will be clobbered by __main, and possibly pushed + on the stack. */ + if (cache->saved_regs[I386_ECX_REGNUM] != -1) + { + CORE_ADDR ecx = cache->base + cache->saved_regs[I386_ECX_REGNUM]; + read_memory (ecx, buf, 4); + } + else + frame_unwind_register (next_frame, I386_ECX_REGNUM, buf); + + cache->saved_sp = extract_unsigned_integer (buf, 4); } if (cache->locals < 0) @@ -1233,12 +1527,23 @@ i386_frame_base_address (struct frame_in return cache->base; } +static CORE_ADDR +i386_frame_args_address (struct frame_info *next_frame, void **this_cache) +{ + struct i386_frame_cache *cache = i386_frame_cache (next_frame, this_cache); + + if (cache->stack_align) + return cache->saved_sp; + + return cache->base; +} + static const struct frame_base i386_frame_base = { &i386_frame_unwind, - i386_frame_base_address, - i386_frame_base_address, - i386_frame_base_address + i386_frame_base_address, /* base */ + i386_frame_base_address, /* locals */ + i386_frame_args_address /* args */ }; static struct frame_id Index: src/gdb/gdbarch.c =================================================================== --- src.orig/gdb/gdbarch.c 2007-10-12 20:25:22.000000000 +0100 +++ src/gdb/gdbarch.c 2007-10-13 03:52:14.000000000 +0100 @@ -186,6 +186,7 @@ struct gdbarch gdbarch_store_return_value_ftype *store_return_value; gdbarch_deprecated_use_struct_convention_ftype *deprecated_use_struct_convention; gdbarch_skip_prologue_ftype *skip_prologue; + gdbarch_skip___main_call_ftype *skip___main_call; gdbarch_inner_than_ftype *inner_than; gdbarch_breakpoint_from_pc_ftype *breakpoint_from_pc; gdbarch_adjust_breakpoint_address_ftype *adjust_breakpoint_address; @@ -308,6 +309,7 @@ struct gdbarch startup_gdbarch = 0, /* store_return_value */ 0, /* deprecated_use_struct_convention */ 0, /* skip_prologue */ + 0, /* skip___main_call */ 0, /* inner_than */ 0, /* breakpoint_from_pc */ 0, /* adjust_breakpoint_address */ @@ -553,6 +555,7 @@ verify_gdbarch (struct gdbarch *current_ /* Skip verify of deprecated_use_struct_convention, invalid_p == 0 */ if (current_gdbarch->skip_prologue == 0) fprintf_unfiltered (log, "\n\tskip_prologue"); + /* Skip verify of skip___main_call, has predicate */ if (current_gdbarch->inner_than == 0) fprintf_unfiltered (log, "\n\tinner_than"); if (current_gdbarch->breakpoint_from_pc == 0) @@ -966,6 +969,12 @@ gdbarch_dump (struct gdbarch *current_gd "gdbarch_dump: single_step_through_delay = <0x%lx>\n", (long) current_gdbarch->single_step_through_delay); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_skip___main_call_p() = %d\n", + gdbarch_skip___main_call_p (current_gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: skip___main_call = <0x%lx>\n", + (long) current_gdbarch->skip___main_call); + fprintf_unfiltered (file, "gdbarch_dump: gdbarch_skip_permanent_breakpoint_p() = %d\n", gdbarch_skip_permanent_breakpoint_p (current_gdbarch)); fprintf_unfiltered (file, @@ -2136,6 +2145,30 @@ set_gdbarch_skip_prologue (struct gdbarc } int +gdbarch_skip___main_call_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->skip___main_call != NULL; +} + +CORE_ADDR +gdbarch_skip___main_call (struct gdbarch *gdbarch, CORE_ADDR ip) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->skip___main_call != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_skip___main_call called\n"); + return gdbarch->skip___main_call (gdbarch, ip); +} + +void +set_gdbarch_skip___main_call (struct gdbarch *gdbarch, + gdbarch_skip___main_call_ftype skip___main_call) +{ + gdbarch->skip___main_call = skip___main_call; +} + +int gdbarch_inner_than (struct gdbarch *gdbarch, CORE_ADDR lhs, CORE_ADDR rhs) { gdb_assert (gdbarch != NULL); Index: src/gdb/gdbarch.h =================================================================== --- src.orig/gdb/gdbarch.h 2007-10-12 20:25:22.000000000 +0100 +++ src/gdb/gdbarch.h 2007-10-13 03:45:06.000000000 +0100 @@ -388,6 +388,12 @@ typedef CORE_ADDR (gdbarch_skip_prologue extern CORE_ADDR gdbarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR ip); extern void set_gdbarch_skip_prologue (struct gdbarch *gdbarch, gdbarch_skip_prologue_ftype *skip_prologue); +extern int gdbarch_skip___main_call_p (struct gdbarch *gdbarch); + +typedef CORE_ADDR (gdbarch_skip___main_call_ftype) (struct gdbarch *gdbarch, CORE_ADDR ip); +extern CORE_ADDR gdbarch_skip___main_call (struct gdbarch *gdbarch, CORE_ADDR ip); +extern void set_gdbarch_skip___main_call (struct gdbarch *gdbarch, gdbarch_skip___main_call_ftype *skip___main_call); + typedef int (gdbarch_inner_than_ftype) (CORE_ADDR lhs, CORE_ADDR rhs); extern int gdbarch_inner_than (struct gdbarch *gdbarch, CORE_ADDR lhs, CORE_ADDR rhs); extern void set_gdbarch_inner_than (struct gdbarch *gdbarch, gdbarch_inner_than_ftype *inner_than); Index: src/gdb/gdbarch.sh =================================================================== --- src.orig/gdb/gdbarch.sh 2007-10-12 20:25:22.000000000 +0100 +++ src/gdb/gdbarch.sh 2007-10-13 03:42:14.000000000 +0100 @@ -514,6 +514,7 @@ f::void:store_return_value:struct type * f::int:deprecated_use_struct_convention:int gcc_p, struct type *value_type:gcc_p, value_type::generic_use_struct_convention::0 f::CORE_ADDR:skip_prologue:CORE_ADDR ip:ip:0:0 +M::CORE_ADDR:skip___main_call:CORE_ADDR ip:ip f::int:inner_than:CORE_ADDR lhs, CORE_ADDR rhs:lhs, rhs:0:0 f::const gdb_byte *:breakpoint_from_pc:CORE_ADDR *pcptr, int *lenptr:pcptr, lenptr::0: M::CORE_ADDR:adjust_breakpoint_address:CORE_ADDR bpaddr:bpaddr Index: src/gdb/i386-cygwin-tdep.c =================================================================== --- src.orig/gdb/i386-cygwin-tdep.c 2007-09-22 17:08:46.000000000 +0100 +++ src/gdb/i386-cygwin-tdep.c 2007-10-13 03:22:58.000000000 +0100 @@ -225,6 +225,8 @@ i386_cygwin_init_abi (struct gdbarch_inf set_gdbarch_skip_trampoline_code (gdbarch, i386_cygwin_skip_trampoline_code); + set_gdbarch_skip___main_call (gdbarch, i386_skip___main_call); + tdep->struct_return = reg_struct_return; tdep->gregset_reg_offset = i386_win32_gregset_reg_offset; Index: src/gdb/i386-tdep.h =================================================================== --- src.orig/gdb/i386-tdep.h 2007-09-22 17:08:46.000000000 +0100 +++ src/gdb/i386-tdep.h 2007-10-13 03:29:48.000000000 +0100 @@ -167,6 +167,9 @@ extern struct type *i386_sse_type (struc /* Functions exported from i386-tdep.c. */ extern CORE_ADDR i386_pe_skip_trampoline_code (CORE_ADDR pc, char *name); +extern CORE_ADDR i386_skip___main_call (struct gdbarch *gdbarch, CORE_ADDR pc); + + /* Return the name of register REGNUM. */ extern char const *i386_register_name (int regnum);