struct i386_frame_cache *cache)
{
unsigned char op;
+ int skip = 0;
if (current_pc <= pc)
return current_pc;
if (current_pc <= pc + 1)
return current_pc;
- /* Check for `movl %esp, %ebp' -- can be written in two ways. */
op = read_memory_unsigned_integer (pc + 1, 1);
+
+ /* Check for some special instructions that might be migrated
+ by GCC into the prologue. We check for
+
+ xorl %ebx, %ebx
+ xorl %ecx, %ecx
+ xorl %edx, %edx
+
+ and the equivalent
+
+ subl %ebx, %ebx
+ subl %ecx, %ecx
+ subl %edx, %edx
+
+ Make sure we only skip these instructions if we later see the
+ `movl %esp, %ebp' that actually sets up the frame. */
+ while (op == 0x29 || op == 0x31)
+ {
+ op = read_memory_unsigned_integer (pc + skip + 2, 1);
+ switch (op)
+ {
+ case 0xdb: /* %ebx */
+ case 0xc9: /* %ecx */
+ case 0xd2: /* %edx */
+ skip += 2;
+ break;
+ default:
+ return pc + 1;
+ }
+
+ op = read_memory_unsigned_integer (pc + skip + 1, 1);
+ }
+
+ /* Check for `movl %esp, %ebp' -- can be written in two ways. */
switch (op)
{
case 0x8b:
- if (read_memory_unsigned_integer (pc + 2, 1) != 0xec)
+ if (read_memory_unsigned_integer (pc + skip + 2, 1) != 0xec)
return pc + 1;
break;
case 0x89:
- if (read_memory_unsigned_integer (pc + 2, 1) != 0xe5)
+ if (read_memory_unsigned_integer (pc + skip + 2, 1) != 0xe5)
return pc + 1;
break;
default:
return pc + 1;
}
- /* OK, we actually have a frame. We just don't know how large it is
- yet. Set its size to zero. We'll adjust it if necessary. */
+ /* OK, we actually have a frame. We just don't know how large
+ it is yet. Set its size to zero. We'll adjust it if
+ necessary. We also now commit to skipping the special
+ instructions mentioned before. */
cache->locals = 0;
+ pc += skip;
/* If that's all, return now. */
if (current_pc <= pc + 3)
if (cache->base == 0)
return;
+ /* See the end of i386_push_dummy_call. */
(*this_id) = frame_id_build (cache->base + 8, cache->pc);
}
};
static const struct frame_unwind *
-i386_frame_p (CORE_ADDR pc)
+i386_frame_sniffer (struct frame_info *next_frame)
{
return &i386_frame_unwind;
}
struct i386_frame_cache *cache =
i386_sigtramp_frame_cache (next_frame, this_cache);
+ /* See the end of i386_push_dummy_call. */
(*this_id) = frame_id_build (cache->base + 8, frame_pc_unwind (next_frame));
}
};
static const struct frame_unwind *
-i386_sigtramp_frame_p (CORE_ADDR pc)
+i386_sigtramp_frame_sniffer (struct frame_info *next_frame)
{
+ CORE_ADDR pc = frame_pc_unwind (next_frame);
char *name;
/* We shouldn't even bother to try if the OSABI didn't register
i386_frame_base_address
};
-static void
-i386_save_dummy_frame_tos (CORE_ADDR sp)
-{
- generic_save_dummy_frame_tos (sp + 8);
-}
-
static struct frame_id
i386_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
frame_unwind_register (next_frame, I386_EBP_REGNUM, buf);
fp = extract_unsigned_integer (buf, 4);
+ /* See the end of i386_push_dummy_call. */
return frame_id_build (fp + 8, frame_pc_unwind (next_frame));
}
\f
/* ...and fake a frame pointer. */
regcache_cooked_write (regcache, I386_EBP_REGNUM, buf);
- return sp;
+ /* MarkK wrote: This "+ 8" is all over the place:
+ (i386_frame_this_id, i386_sigtramp_frame_this_id,
+ i386_unwind_dummy_id). It's there, since all frame unwinders for
+ a given target have to agree (within a certain margin) on the
+ defenition of the stack address of a frame. Otherwise
+ frame_id_inner() won't work correctly. Since DWARF2/GCC uses the
+ stack address *before* the function call as a frame's CFA. On
+ the i386, when %ebp is used as a frame pointer, the offset
+ between the contents %ebp and the CFA as defined by GCC. */
+ return sp + 8;
}
/* These registers are used for returning integers (and on some
else
regcache_raw_write (regcache, regnum, buf);
}
+\f
+
+/* These registers don't have pervasive standard uses. Move them to
+ i386-tdep.h if necessary. */
+
+#define I386_EBX_REGNUM 3 /* %ebx */
+#define I386_ECX_REGNUM 1 /* %ecx */
+#define I386_ESI_REGNUM 6 /* %esi */
+#define I386_EDI_REGNUM 7 /* %edi */
+
+/* Return the register number of the register allocated by GCC after
+ REGNUM, or -1 if there is no such register. */
+
+static int
+i386_next_regnum (int regnum)
+{
+ /* GCC allocates the registers in the order:
+
+ %eax, %edx, %ecx, %ebx, %esi, %edi, %ebp, %esp, ...
+
+ Since storing a variable in %esp doesn't make any sense we return
+ -1 for %ebp and for %esp itself. */
+ static int next_regnum[] =
+ {
+ I386_EDX_REGNUM, /* Slot for %eax. */
+ I386_EBX_REGNUM, /* Slot for %ecx. */
+ I386_ECX_REGNUM, /* Slot for %edx. */
+ I386_ESI_REGNUM, /* Slot for %ebx. */
+ -1, -1, /* Slots for %esp and %ebp. */
+ I386_EDI_REGNUM, /* Slot for %esi. */
+ I386_EBP_REGNUM /* Slot for %edi. */
+ };
+
+ if (regnum >= 0 && regnum < sizeof (next_regnum) / sizeof (next_regnum[0]))
+ return next_regnum[regnum];
+
+ return -1;
+}
-/* Return true iff register REGNUM's virtual format is different from
- its raw format. Note that this definition assumes that the host
- supports IEEE 32-bit floats, since it doesn't say that SSE
- registers need conversion. Even if we can't find a counterexample,
- this is still sloppy. */
+/* Return nonzero if a value of type TYPE stored in register REGNUM
+ needs any special handling. */
static int
-i386_register_convertible (int regnum)
+i386_convert_register_p (int regnum, struct type *type)
{
+ int len = TYPE_LENGTH (type);
+
+ /* Values may be spread across multiple registers. Most debugging
+ formats aren't expressive enough to specify the locations, so
+ some heuristics is involved. Right now we only handle types that
+ have a length that is a multiple of the word size, since GCC
+ doesn't seem to put any other types into registers. */
+ if (len > 4 && len % 4 == 0)
+ {
+ int last_regnum = regnum;
+
+ while (len > 4)
+ {
+ last_regnum = i386_next_regnum (last_regnum);
+ len -= 4;
+ }
+
+ if (last_regnum != -1)
+ return 1;
+ }
+
return i386_fp_regnum_p (regnum);
}
-/* Convert data from raw format for register REGNUM in buffer FROM to
- virtual format with type TYPE in buffer TO. */
+/* Read a value of type TYPE from register REGNUM in frame FRAME, and
+ return its contents in TO. */
static void
-i386_register_convert_to_virtual (int regnum, struct type *type,
- char *from, char *to)
+i386_register_to_value (struct frame_info *frame, int regnum,
+ struct type *type, void *to)
{
- gdb_assert (i386_fp_regnum_p (regnum));
+ int len = TYPE_LENGTH (type);
+ char *buf = to;
- /* We only support floating-point values. */
- if (TYPE_CODE (type) != TYPE_CODE_FLT)
+ /* FIXME: kettenis/20030609: What should we do if REGNUM isn't
+ available in FRAME (i.e. if it wasn't saved)? */
+
+ if (i386_fp_regnum_p (regnum))
{
- warning ("Cannot convert floating-point register value "
- "to non-floating-point type.");
- memset (to, 0, TYPE_LENGTH (type));
+ i387_register_to_value (frame, regnum, type, to);
return;
}
- /* Convert to TYPE. This should be a no-op if TYPE is equivalent to
- the extended floating-point format used by the FPU. */
- convert_typed_floating (from, builtin_type_i387_ext, to, type);
+ /* Read a value spread accross multiple registers. */
+
+ gdb_assert (len > 4 && len % 4 == 0);
+
+ while (len > 0)
+ {
+ gdb_assert (regnum != -1);
+ gdb_assert (register_size (current_gdbarch, regnum) == 4);
+
+ frame_read_register (frame, regnum, buf);
+ regnum = i386_next_regnum (regnum);
+ len -= 4;
+ buf += 4;
+ }
}
-/* Convert data from virtual format with type TYPE in buffer FROM to
- raw format for register REGNUM in buffer TO. */
+/* Write the contents FROM of a value of type TYPE into register
+ REGNUM in frame FRAME. */
static void
-i386_register_convert_to_raw (struct type *type, int regnum,
- char *from, char *to)
+i386_value_to_register (struct frame_info *frame, int regnum,
+ struct type *type, const void *from)
{
- gdb_assert (i386_fp_regnum_p (regnum));
+ int len = TYPE_LENGTH (type);
+ const char *buf = from;
- /* We only support floating-point values. */
- if (TYPE_CODE (type) != TYPE_CODE_FLT)
+ if (i386_fp_regnum_p (regnum))
{
- warning ("Cannot convert non-floating-point type "
- "to floating-point register value.");
- memset (to, 0, TYPE_LENGTH (type));
+ i387_value_to_register (frame, regnum, type, from);
return;
}
- /* Convert from TYPE. This should be a no-op if TYPE is equivalent
- to the extended floating-point format used by the FPU. */
- convert_typed_floating (from, type, to, builtin_type_i387_ext);
+ /* Write a value spread accross multiple registers. */
+
+ gdb_assert (len > 4 && len % 4 == 0);
+
+ while (len > 0)
+ {
+ gdb_assert (regnum != -1);
+ gdb_assert (register_size (current_gdbarch, regnum) == 4);
+
+ put_frame_register (frame, regnum, buf);
+ regnum = i386_next_regnum (regnum);
+ len -= 4;
+ buf += 4;
+ }
}
-\f
+\f
+
#ifdef STATIC_TRANSFORM_NAME
/* SunPRO encodes the static variables. This is not related to C++
}
\f
+/* Get the ith function argument for the current function. */
+static CORE_ADDR
+i386_fetch_pointer_argument (struct frame_info *frame, int argi,
+ struct type *type)
+{
+ CORE_ADDR stack;
+ frame_read_register (frame, SP_REGNUM, &stack);
+ return read_memory_unsigned_integer (stack + (4 * (argi + 1)), 4);
+}
+
+\f
static struct gdbarch *
i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
/* Call dummy code. */
set_gdbarch_push_dummy_call (gdbarch, i386_push_dummy_call);
- set_gdbarch_register_convertible (gdbarch, i386_register_convertible);
- set_gdbarch_register_convert_to_virtual (gdbarch,
- i386_register_convert_to_virtual);
- set_gdbarch_register_convert_to_raw (gdbarch, i386_register_convert_to_raw);
+ set_gdbarch_convert_register_p (gdbarch, i386_convert_register_p);
+ set_gdbarch_register_to_value (gdbarch, i386_register_to_value);
+ set_gdbarch_value_to_register (gdbarch, i386_value_to_register);
set_gdbarch_extract_return_value (gdbarch, i386_extract_return_value);
set_gdbarch_store_return_value (gdbarch, i386_store_return_value);
set_gdbarch_function_start_offset (gdbarch, 0);
set_gdbarch_frame_args_skip (gdbarch, 8);
- set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown);
set_gdbarch_pc_in_sigtramp (gdbarch, i386_pc_in_sigtramp);
/* Wire in the MMX registers. */
set_gdbarch_print_insn (gdbarch, i386_print_insn);
set_gdbarch_unwind_dummy_id (gdbarch, i386_unwind_dummy_id);
- set_gdbarch_save_dummy_frame_tos (gdbarch, i386_save_dummy_frame_tos);
set_gdbarch_unwind_pc (gdbarch, i386_unwind_pc);
i386_add_reggroups (gdbarch);
set_gdbarch_register_reggroup_p (gdbarch, i386_register_reggroup_p);
+ /* Helper for function argument information. */
+ set_gdbarch_fetch_pointer_argument (gdbarch, i386_fetch_pointer_argument);
+
/* Hook in the DWARF CFI frame unwinder. */
- frame_unwind_append_predicate (gdbarch, dwarf2_frame_p);
- set_gdbarch_dwarf2_build_frame_info (gdbarch, dwarf2_build_frame_info);
+ frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
frame_base_set_default (gdbarch, &i386_frame_base);
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
- frame_unwind_append_predicate (gdbarch, i386_sigtramp_frame_p);
- frame_unwind_append_predicate (gdbarch, i386_frame_p);
+ frame_unwind_append_sniffer (gdbarch, i386_sigtramp_frame_sniffer);
+ frame_unwind_append_sniffer (gdbarch, i386_frame_sniffer);
return gdbarch;
}