/* Target-dependent code for GNU/Linux running on i386's, for GDB.
- Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GDB.
#include "value.h"
#include "regcache.h"
#include "inferior.h"
+#include "reggroups.h"
/* For i386_linux_skip_solib_resolver. */
#include "symtab.h"
#include "solib-svr4.h" /* For struct link_map_offsets. */
+#include "osabi.h"
+
+#include "i386-tdep.h"
+#include "i386-linux-tdep.h"
+
/* Return the name of register REG. */
-char *
+static const char *
i386_linux_register_name (int reg)
{
/* Deal with the extra "orig_eax" pseudo register. */
return i386_register_name (reg);
}
-int
-i386_linux_register_byte (int reg)
+/* Return non-zero, when the register is in the corresponding register
+ group. Put the LINUX_ORIG_EAX register in the system group. */
+static int
+i386_linux_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+ struct reggroup *group)
{
- /* Deal with the extra "orig_eax" pseudo register. */
- if (reg == I386_LINUX_ORIG_EAX_REGNUM)
- return (i386_register_byte (I386_LINUX_ORIG_EAX_REGNUM - 1)
- + i386_register_raw_size (I386_LINUX_ORIG_EAX_REGNUM - 1));
-
- return i386_register_byte (reg);
+ if (regnum == I386_LINUX_ORIG_EAX_REGNUM)
+ return (group == system_reggroup
+ || group == save_reggroup
+ || group == restore_reggroup);
+ return i386_register_reggroup_p (gdbarch, regnum, group);
}
-int
-i386_linux_register_raw_size (int reg)
-{
- /* Deal with the extra "orig_eax" pseudo register. */
- if (reg == I386_LINUX_ORIG_EAX_REGNUM)
- return 4;
-
- return i386_register_raw_size (reg);
-}
\f
/* Recognizing signal handler frames. */
The instruction sequence for normal signals is
pop %eax
- mov $0x77,%eax
+ mov $0x77, %eax
int $0x80
or 0x58 0xb8 0x77 0x00 0x00 0x00 0xcd 0x80.
to the ones used by the kernel. Therefore, these trampolines are
supported too. */
-#define LINUX_SIGTRAMP_INSN0 (0x58) /* pop %eax */
-#define LINUX_SIGTRAMP_OFFSET0 (0)
-#define LINUX_SIGTRAMP_INSN1 (0xb8) /* mov $NNNN,%eax */
-#define LINUX_SIGTRAMP_OFFSET1 (1)
-#define LINUX_SIGTRAMP_INSN2 (0xcd) /* int */
-#define LINUX_SIGTRAMP_OFFSET2 (6)
+#define LINUX_SIGTRAMP_INSN0 0x58 /* pop %eax */
+#define LINUX_SIGTRAMP_OFFSET0 0
+#define LINUX_SIGTRAMP_INSN1 0xb8 /* mov $NNNN, %eax */
+#define LINUX_SIGTRAMP_OFFSET1 1
+#define LINUX_SIGTRAMP_INSN2 0xcd /* int */
+#define LINUX_SIGTRAMP_OFFSET2 6
static const unsigned char linux_sigtramp_code[] =
{
LINUX_SIGTRAMP_INSN0, /* pop %eax */
- LINUX_SIGTRAMP_INSN1, 0x77, 0x00, 0x00, 0x00, /* mov $0x77,%eax */
+ LINUX_SIGTRAMP_INSN1, 0x77, 0x00, 0x00, 0x00, /* mov $0x77, %eax */
LINUX_SIGTRAMP_INSN2, 0x80 /* int $0x80 */
};
/* This function does the same for RT signals. Here the instruction
sequence is
- mov $0xad,%eax
+ mov $0xad, %eax
int $0x80
or 0xb8 0xad 0x00 0x00 0x00 0xcd 0x80.
The effect is to call the system call rt_sigreturn. */
-#define LINUX_RT_SIGTRAMP_INSN0 (0xb8) /* mov $NNNN,%eax */
-#define LINUX_RT_SIGTRAMP_OFFSET0 (0)
-#define LINUX_RT_SIGTRAMP_INSN1 (0xcd) /* int */
-#define LINUX_RT_SIGTRAMP_OFFSET1 (5)
+#define LINUX_RT_SIGTRAMP_INSN0 0xb8 /* mov $NNNN, %eax */
+#define LINUX_RT_SIGTRAMP_OFFSET0 0
+#define LINUX_RT_SIGTRAMP_INSN1 0xcd /* int */
+#define LINUX_RT_SIGTRAMP_OFFSET1 5
static const unsigned char linux_rt_sigtramp_code[] =
{
- LINUX_RT_SIGTRAMP_INSN0, 0xad, 0x00, 0x00, 0x00, /* mov $0xad,%eax */
+ LINUX_RT_SIGTRAMP_INSN0, 0xad, 0x00, 0x00, 0x00, /* mov $0xad, %eax */
LINUX_RT_SIGTRAMP_INSN1, 0x80 /* int $0x80 */
};
/* Return whether PC is in a GNU/Linux sigtramp routine. */
-int
-i386_linux_in_sigtramp (CORE_ADDR pc, char *name)
+static int
+i386_linux_pc_in_sigtramp (CORE_ADDR pc, char *name)
{
- if (name)
- return STREQ ("__restore", name) || STREQ ("__restore_rt", name);
-
- return (i386_linux_sigtramp_start (pc) != 0
- || i386_linux_rt_sigtramp_start (pc) != 0);
+ /* If we have NAME, we can optimize the search. The trampolines are
+ named __restore and __restore_rt. However, they aren't dynamically
+ exported from the shared C library, so the trampoline may appear to
+ be part of the preceding function. This should always be sigaction,
+ __sigaction, or __libc_sigaction (all aliases to the same function). */
+ if (name == NULL || strstr (name, "sigaction") != NULL)
+ return (i386_linux_sigtramp_start (pc) != 0
+ || i386_linux_rt_sigtramp_start (pc) != 0);
+
+ return (strcmp ("__restore", name) == 0
+ || strcmp ("__restore_rt", name) == 0);
}
-/* Assuming FRAME is for a GNU/Linux sigtramp routine, return the
- address of the associated sigcontext structure. */
+/* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>. */
+#define I386_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 20
-CORE_ADDR
-i386_linux_sigcontext_addr (struct frame_info *frame)
+/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp
+ routine, return the address of the associated sigcontext structure. */
+
+static CORE_ADDR
+i386_linux_sigcontext_addr (struct frame_info *next_frame)
{
CORE_ADDR pc;
+ CORE_ADDR sp;
+ char buf[4];
- pc = i386_linux_sigtramp_start (frame->pc);
+ frame_unwind_register (next_frame, I386_ESP_REGNUM, buf);
+ sp = extract_unsigned_integer (buf, 4);
+
+ pc = i386_linux_sigtramp_start (frame_pc_unwind (next_frame));
if (pc)
{
- CORE_ADDR sp;
-
- if (frame->next)
- /* If this isn't the top frame, the next frame must be for the
- signal handler itself. The sigcontext structure lives on
- the stack, right after the signum argument. */
- return frame->next->frame + 12;
-
- /* This is the top frame. We'll have to find the address of the
- sigcontext structure by looking at the stack pointer. Keep
- in mind that the first instruction of the sigtramp code is
- "pop %eax". If the PC is at this instruction, adjust the
- returned value accordingly. */
- sp = read_register (SP_REGNUM);
- if (pc == frame->pc)
+ /* The sigcontext structure lives on the stack, right after
+ the signum argument. We determine the address of the
+ sigcontext structure by looking at the frame's stack
+ pointer. Keep in mind that the first instruction of the
+ sigtramp code is "pop %eax". If the PC is after this
+ instruction, adjust the returned value accordingly. */
+ if (pc == frame_pc_unwind (next_frame))
return sp + 4;
return sp;
}
- pc = i386_linux_rt_sigtramp_start (frame->pc);
+ pc = i386_linux_rt_sigtramp_start (frame_pc_unwind (next_frame));
if (pc)
{
- if (frame->next)
- /* If this isn't the top frame, the next frame must be for the
- signal handler itself. The sigcontext structure is part of
- the user context. A pointer to the user context is passed
- as the third argument to the signal handler. */
- return read_memory_integer (frame->next->frame + 16, 4) + 20;
-
- /* This is the top frame. Again, use the stack pointer to find
- the address of the sigcontext structure. */
- return read_memory_integer (read_register (SP_REGNUM) + 8, 4) + 20;
+ CORE_ADDR ucontext_addr;
+
+ /* The sigcontext structure is part of the user context. A
+ pointer to the user context is passed as the third argument
+ to the signal handler. */
+ read_memory (sp + 8, buf, 4);
+ ucontext_addr = extract_unsigned_integer (buf, 4) + 20;
+ return ucontext_addr + I386_LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
}
error ("Couldn't recognize signal trampoline.");
return 0;
}
-/* Offset to saved PC in sigcontext, from <asm/sigcontext.h>. */
-#define LINUX_SIGCONTEXT_PC_OFFSET (56)
-
-/* Assuming FRAME is for a GNU/Linux sigtramp routine, return the
- saved program counter. */
-
-static CORE_ADDR
-i386_linux_sigtramp_saved_pc (struct frame_info *frame)
-{
- CORE_ADDR addr;
- addr = i386_linux_sigcontext_addr (frame);
- return read_memory_integer (addr + LINUX_SIGCONTEXT_PC_OFFSET, 4);
-}
-
-/* Offset to saved SP in sigcontext, from <asm/sigcontext.h>. */
-#define LINUX_SIGCONTEXT_SP_OFFSET (28)
-
-/* Assuming FRAME is for a GNU/Linux sigtramp routine, return the
- saved stack pointer. */
-
-static CORE_ADDR
-i386_linux_sigtramp_saved_sp (struct frame_info *frame)
-{
- CORE_ADDR addr;
- addr = i386_linux_sigcontext_addr (frame);
- return read_memory_integer (addr + LINUX_SIGCONTEXT_SP_OFFSET, 4);
-}
-
-/* Signal trampolines don't have a meaningful frame. As in
- "i386/tm-i386.h", the frame pointer value we use is actually the
- frame pointer of the calling frame -- that is, the frame which was
- in progress when the signal trampoline was entered. GDB mostly
- treats this frame pointer value as a magic cookie. We detect the
- case of a signal trampoline by looking at the SIGNAL_HANDLER_CALLER
- field, which is set based on PC_IN_SIGTRAMP.
-
- When a signal trampoline is invoked from a frameless function, we
- essentially have two frameless functions in a row. In this case,
- we use the same magic cookie for three frames in a row. We detect
- this case by seeing whether the next frame has
- SIGNAL_HANDLER_CALLER set, and, if it does, checking whether the
- current frame is actually frameless. In this case, we need to get
- the PC by looking at the SP register value stored in the signal
- context.
-
- This should work in most cases except in horrible situations where
- a signal occurs just as we enter a function but before the frame
- has been set up. */
-
-#define FRAMELESS_SIGNAL(frame) \
- ((frame)->next != NULL \
- && (frame)->next->signal_handler_caller \
- && frameless_look_for_prologue (frame))
-
-CORE_ADDR
-i386_linux_frame_chain (struct frame_info *frame)
-{
- if (frame->signal_handler_caller || FRAMELESS_SIGNAL (frame))
- return frame->frame;
-
- if (! inside_entry_file (frame->pc))
- return read_memory_unsigned_integer (frame->frame, 4);
-
- return 0;
-}
-
-/* Return the saved program counter for FRAME. */
-
-CORE_ADDR
-i386_linux_frame_saved_pc (struct frame_info *frame)
-{
- if (frame->signal_handler_caller)
- return i386_linux_sigtramp_saved_pc (frame);
-
- if (FRAMELESS_SIGNAL (frame))
- {
- CORE_ADDR sp = i386_linux_sigtramp_saved_sp (frame->next);
- return read_memory_unsigned_integer (sp, 4);
- }
-
- return read_memory_unsigned_integer (frame->frame + 4, 4);
-}
-
-/* Immediately after a function call, return the saved pc. */
-
-CORE_ADDR
-i386_linux_saved_pc_after_call (struct frame_info *frame)
-{
- if (frame->signal_handler_caller)
- return i386_linux_sigtramp_saved_pc (frame);
-
- return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
-}
-
/* Set the program counter for process PTID to PC. */
-void
+static void
i386_linux_write_pc (CORE_ADDR pc, ptid_t ptid)
{
- write_register_pid (PC_REGNUM, pc, ptid);
+ write_register_pid (I386_EIP_REGNUM, pc, ptid);
/* We must be careful with modifying the program counter. If we
just interrupted a system call, the kernel might try to restart
be considered too special-purpose for general consumption. */
static struct minimal_symbol *
-find_minsym_and_objfile (char *name, struct objfile **objfile_p)
+find_minsym_and_objfile (char *name, struct objfile **objfilep)
{
struct objfile *objfile;
ALL_OBJFILE_MSYMBOLS (objfile, msym)
{
- if (SYMBOL_NAME (msym)
- && STREQ (SYMBOL_NAME (msym), name))
+ if (SYMBOL_LINKAGE_NAME (msym)
+ && strcmp (SYMBOL_LINKAGE_NAME (msym), name) == 0)
{
- *objfile_p = objfile;
+ *objfilep = objfile;
return msym;
}
}
}
static CORE_ADDR
-skip_hurd_resolver (CORE_ADDR pc)
+skip_gnu_resolver (CORE_ADDR pc)
{
- /* The HURD dynamic linker is part of the GNU C library, so many
+ /* The GNU dynamic linker is part of the GNU C library, so many
GNU/Linux distributions use it. (All ELF versions, as far as I
know.) An unresolved PLT entry points to "_dl_runtime_resolve",
which calls "fixup" to patch the PLT, and then passes control to
= lookup_minimal_symbol ("fixup", NULL, objfile);
if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc)
- return (SAVED_PC_AFTER_CALL (get_current_frame ()));
+ return frame_pc_unwind (get_current_frame ());
}
return 0;
CORE_ADDR result;
/* Plug in functions for other kinds of resolvers here. */
- result = skip_hurd_resolver (pc);
+ result = skip_gnu_resolver (pc);
if (result)
return result;
from a GDB that was not built on an GNU/Linux x86 host (for cross
debugging). */
-struct link_map_offsets *
+static struct link_map_offsets *
i386_linux_svr4_fetch_link_map_offsets (void)
{
static struct link_map_offsets lmo;
return lmp;
}
+\f
+
+/* From <asm/sigcontext.h>. */
+static int i386_linux_sc_reg_offset[I386_NUM_GREGS] =
+{
+ 11 * 4, /* %eax */
+ 10 * 4, /* %ecx */
+ 9 * 4, /* %edx */
+ 8 * 4, /* %ebx */
+ 7 * 4, /* %esp */
+ 6 * 4, /* %ebp */
+ 5 * 4, /* %esi */
+ 4 * 4, /* %edi */
+ 14 * 4, /* %eip */
+ 16 * 4, /* %eflags */
+ 15 * 4, /* %cs */
+ 18 * 4, /* %ss */
+ 3 * 4, /* %ds */
+ 2 * 4, /* %es */
+ 1 * 4, /* %fs */
+ 0 * 4 /* %gs */
+};
+
+static void
+i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* GNU/Linux uses ELF. */
+ i386_elf_init_abi (info, gdbarch);
+
+ /* We support the SSE registers on GNU/Linux. */
+ tdep->num_xmm_regs = I386_NUM_XREGS - 1;
+ /* set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS); */
+
+ /* Since we have the extra "orig_eax" register on GNU/Linux, we have
+ to adjust a few things. */
+
+ set_gdbarch_write_pc (gdbarch, i386_linux_write_pc);
+ set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS + 1);
+ set_gdbarch_register_name (gdbarch, i386_linux_register_name);
+ set_gdbarch_register_reggroup_p (gdbarch, i386_linux_register_reggroup_p);
+
+ tdep->jb_pc_offset = 20; /* From <bits/setjmp.h>. */
+
+ tdep->sigcontext_addr = i386_linux_sigcontext_addr;
+ tdep->sc_reg_offset = i386_linux_sc_reg_offset;
+ tdep->sc_num_regs = I386_NUM_GREGS;
+
+ /* When the i386 Linux kernel calls a signal handler, the return
+ address points to a bit of code on the stack. This function is
+ used to identify this bit of code as a signal trampoline in order
+ to support backtracing through calls to signal handlers. */
+ set_gdbarch_pc_in_sigtramp (gdbarch, i386_linux_pc_in_sigtramp);
+
+ set_solib_svr4_fetch_link_map_offsets (gdbarch,
+ i386_linux_svr4_fetch_link_map_offsets);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern void _initialize_i386_linux_tdep (void);
+
+void
+_initialize_i386_linux_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_LINUX,
+ i386_linux_init_abi);
+}