/* Target-dependent code for the GNU Hurd.
- Copyright (C) 2002-2017 Free Software Foundation, Inc.
+ Copyright (C) 2002-2022 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "gdbcore.h"
#include "osabi.h"
#include "solib-svr4.h"
#include "i386-tdep.h"
+/* Recognizing signal handler frames. */
+
+/* When the GNU/Hurd libc calls a signal handler, the return address points
+ inside the trampoline assembly snippet.
+
+ If the trampoline function name can not be identified, we resort to reading
+ memory from the process in order to identify it. */
+
+static const gdb_byte gnu_sigtramp_code[] =
+{
+/* rpc_wait_trampoline: */
+ 0xb8, 0xe7, 0xff, 0xff, 0xff, /* mov $-25,%eax */
+ 0x9a, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, /* lcall $7,$0 */
+ 0x89, 0x01, /* movl %eax, (%ecx) */
+ 0x89, 0xdc, /* movl %ebx, %esp */
+
+/* trampoline: */
+ 0xff, 0xd2, /* call *%edx */
+/* RA HERE */
+ 0x83, 0xc4, 0x0c, /* addl $12, %esp */
+ 0xc3, /* ret */
+
+/* firewall: */
+ 0xf4, /* hlt */
+};
+
+#define GNU_SIGTRAMP_LEN (sizeof gnu_sigtramp_code)
+#define GNU_SIGTRAMP_TAIL 5 /* length of tail after RA */
+
+/* If THIS_FRAME is a sigtramp routine, return the address of the
+ start of the routine. Otherwise, return 0. */
+
+static CORE_ADDR
+i386_gnu_sigtramp_start (struct frame_info *this_frame)
+{
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ gdb_byte buf[GNU_SIGTRAMP_LEN];
+
+ if (!safe_frame_unwind_memory (this_frame,
+ pc + GNU_SIGTRAMP_TAIL - GNU_SIGTRAMP_LEN,
+ buf))
+ return 0;
+
+ if (memcmp (buf, gnu_sigtramp_code, GNU_SIGTRAMP_LEN) != 0)
+ return 0;
+
+ return pc;
+}
+
+/* Return whether THIS_FRAME corresponds to a GNU/Linux sigtramp
+ routine. */
+
+static int
+i386_gnu_sigtramp_p (struct frame_info *this_frame)
+{
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ const char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+
+ /* If we have a NAME, we can check for the trampoline function */
+ if (name != NULL && strcmp (name, "trampoline") == 0)
+ return 1;
+
+ return i386_gnu_sigtramp_start (this_frame) != 0;
+}
+
+/* Offset to sc_i386_thread_state in sigcontext, from <bits/sigcontext.h>. */
+#define I386_GNU_SIGCONTEXT_THREAD_STATE_OFFSET 20
+
+/* Assuming THIS_FRAME is a GNU/Linux sigtramp routine, return the
+ address of the associated sigcontext structure. */
+
+static CORE_ADDR
+i386_gnu_sigcontext_addr (struct frame_info *this_frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR pc;
+ CORE_ADDR sp;
+ gdb_byte buf[4];
+
+ get_frame_register (this_frame, I386_ESP_REGNUM, buf);
+ sp = extract_unsigned_integer (buf, 4, byte_order);
+
+ pc = i386_gnu_sigtramp_start (this_frame);
+ if (pc)
+ {
+ CORE_ADDR sigcontext_addr;
+
+ /* The sigcontext structure address is passed as the third argument to
+ the signal handler. */
+ read_memory (sp + 8, buf, 4);
+ sigcontext_addr = extract_unsigned_integer (buf, 4, byte_order);
+ return sigcontext_addr + I386_GNU_SIGCONTEXT_THREAD_STATE_OFFSET;
+ }
+
+ error (_("Couldn't recognize signal trampoline."));
+ return 0;
+}
+
+/* Mapping between the general-purpose registers in `struct
+ sigcontext' format (starting at sc_i386_thread_state)
+ and GDB's register cache layout. */
+
+/* From <bits/sigcontext.h>. */
+static int i386_gnu_sc_reg_offset[] =
+{
+ 11 * 4, /* %eax */
+ 10 * 4, /* %ecx */
+ 9 * 4, /* %edx */
+ 8 * 4, /* %ebx */
+ 7 * 4, /* %esp */
+ 6 * 4, /* %ebp */
+ 5 * 4, /* %esi */
+ 4 * 4, /* %edi */
+ 12 * 4, /* %eip */
+ 14 * 4, /* %eflags */
+ 13 * 4, /* %cs */
+ 16 * 4, /* %ss */
+ 3 * 4, /* %ds */
+ 2 * 4, /* %es */
+ 1 * 4, /* %fs */
+ 0 * 4 /* %gs */
+};
+
/* From <sys/ucontext.h>. */
static int i386gnu_gregset_reg_offset[] =
{
static void
i386gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ i386_gdbarch_tdep *tdep = (i386_gdbarch_tdep *) gdbarch_tdep (gdbarch);
/* GNU uses ELF. */
i386_elf_init_abi (info, gdbarch);
tdep->sizeof_gregset = 19 * 4;
tdep->jb_pc_offset = 20; /* From <bits/setjmp.h>. */
-}
-/* Provide a prototype to silence -Wmissing-prototypes. */
-extern void _initialize_i386gnu_tdep (void);
+ tdep->sigtramp_p = i386_gnu_sigtramp_p;
+ tdep->sigcontext_addr = i386_gnu_sigcontext_addr;
+ tdep->sc_reg_offset = i386_gnu_sc_reg_offset;
+ tdep->sc_num_regs = ARRAY_SIZE (i386_gnu_sc_reg_offset);
+}
+void _initialize_i386gnu_tdep ();
void
-_initialize_i386gnu_tdep (void)
+_initialize_i386gnu_tdep ()
{
gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_HURD, i386gnu_init_abi);
}