Re: [rfa] Attach vsyscall support for GNU/Linux

Daniel Jacobowitz
2004-11-05T23:42:36Z

On Mon, Nov 01, 2004 at 09:45:20PM +0100, Mark Kettenis wrote:
> I forgot about it :-(. I had a look just now, but unfortunately it's
> not as easy as I thought. The frame type is currently hard-coded in
> the unwinder. This is wrong, but Andrew thinks it's wrong in a
> different way than I. At least, that's what I think. I'll have to
> learn reading UML diagrams first. I'll throw some Feynman diagrams
> into my next mail to get even with him ;-). This is not going to be a
> simple fix, therefore...

How about this, in the meantime? If you don't like the approach, I'll
wait until we can properly decide the relationship between unwinders
and frame types.

This patch adds a new sniffer, dwarf2_signal_frame_sniffer, which will
only accept the frame if an architecture-specific hook has claimed that
this is a signal frame (i386 GNU/Linux provides one that works by
name). However, we then use the CFI normally - the only difference is
that the result has SIGTRAMP_FRAME as its type. It works beautifully!
Tested on i386-pc-linux-gnu, with vsyscall DSO.

--
Daniel Jacobowitz

2004-11-05 Andrew Cagney <cagney@redhat.com>
Daniel Jacobowitz <dan@debian.org>
Roland McGrath <roland@redhat.com>

* Makefile.in (symfile-mem.o): Update dependencies.
* dwarf2-frame.c (struct dwarf2_frame_ops): Add signal_frame_p.
(dwarf2_frame_set_signal_frame_p, dwarf2_frame_signal_frame_p)
(dwarf2_signal_frame_unwind, dwarf2_signal_frame_sniffer): New.
* dwarf2-frame.h (dwarf2_frame_set_signal_frame_p)
(dwarf2_signal_frame_sniffer): New prototypes.
* i386-linux-tdep.c (i386_linux_dwarf_signal_frame_p): New.
(i386_linux_init_abi): Call dwarf2_frame_set_signal_frame_p.
* i386-tdep.c (i386_gdbarch_init): Append dwarf2_signal_frame_sniffer
before dwarf2_frame_sniffer.
* inf-ptrace.c (inf_ptrace_attach): Call
observer_notify_inferior_created.
* inftarg.c (child_attach): Likewise.
* symfile-mem.c: Include "observer.h", "auxv.h", and "elf/common.h".
(symbol_file_add_from_memory): Take NAME argument. Use it for
the new BFD's filename.
(add_symbol_file_from_memory_command): Update call to
symbol_file_add_from_memory.
(struct symbol_file_add_from_memory_args, add_vsyscall_page)
(symbol_file_add_from_memory_wrapper): New.
(_initialize_symfile_mem): Register add_vsyscall_page as an
inferior_created observer.

Index: gdb/Makefile.in
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/Makefile.in,v
retrieving revision 1.654
diff -u -p -r1.654 Makefile.in
--- gdb/Makefile.in 4 Nov 2004 02:15:19 -0000 1.654
+++ gdb/Makefile.in 5 Nov 2004 20:10:05 -0000
@@ -2619,7 +2619,8 @@ symfile.o: symfile.c $(defs_h) $(bfdlink
$(hashtab_h) $(readline_h) $(gdb_assert_h) $(block_h) \
$(gdb_string_h) $(gdb_stat_h)
symfile-mem.o: symfile-mem.c $(defs_h) $(symtab_h) $(gdbcore_h) \
- $(objfiles_h) $(gdbcmd_h) $(target_h) $(value_h) $(symfile_h)
+ $(objfiles_h) $(gdbcmd_h) $(target_h) $(value_h) $(symfile_h) \
+ $(observer_h) $(auxv_h) $(elf_common_h)
symmisc.o: symmisc.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(bfd_h) \
$(symfile_h) $(objfiles_h) $(breakpoint_h) $(command_h) \
$(gdb_obstack_h) $(language_h) $(bcache_h) $(block_h) $(gdb_regex_h) \
Index: gdb/dwarf2-frame.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2-frame.c,v
retrieving revision 1.41
diff -u -p -r1.41 dwarf2-frame.c
--- gdb/dwarf2-frame.c 4 Nov 2004 21:15:15 -0000 1.41
+++ gdb/dwarf2-frame.c 5 Nov 2004 20:33:55 -0000
@@ -471,6 +471,10 @@ struct dwarf2_frame_ops
{
/* Pre-initialize the register state REG for register REGNUM. */
void (*init_reg) (struct gdbarch *, int, struct dwarf2_frame_state_reg *);
+
+ /* Check whether the frame preceding NEXT_FRAME will be a signal
+ trampoline. */
+ int (*signal_frame_p) (struct gdbarch *, struct frame_info *);
};

/* Default architecture-specific register state initialization
@@ -547,6 +551,33 @@ dwarf2_frame_init_reg (struct gdbarch *g

ops->init_reg (gdbarch, regnum, reg);
}
+
+/* Set the architecture-specific signal trampoline recognition
+ function for GDBARCH to SIGNAL_FRAME_P. */
+
+void
+dwarf2_frame_set_signal_frame_p (struct gdbarch *gdbarch,
+ int (*signal_frame_p) (struct gdbarch *,
+ struct frame_info *))
+{
+ struct dwarf2_frame_ops *ops = gdbarch_data (gdbarch, dwarf2_frame_data);
+
+ ops->signal_frame_p = signal_frame_p;
+}
+
+/* Query the architecture-specific signal frame recognizer for
+ NEXT_FRAME. */
+
+static int
+dwarf2_frame_signal_frame_p (struct gdbarch *gdbarch,
+ struct frame_info *next_frame)
+{
+ struct dwarf2_frame_ops *ops = gdbarch_data (gdbarch, dwarf2_frame_data);
+
+ if (ops->signal_frame_p == NULL)
+ return 0;
+ return ops->signal_frame_p (gdbarch, next_frame);
+}
\f

struct dwarf2_frame_cache
@@ -853,6 +884,28 @@ dwarf2_frame_sniffer (struct frame_info

return NULL;
}
+
+static const struct frame_unwind dwarf2_signal_frame_unwind =
+{
+ SIGTRAMP_FRAME,
+ dwarf2_frame_this_id,
+ dwarf2_frame_prev_register
+};
+
+const struct frame_unwind *
+dwarf2_signal_frame_sniffer (struct frame_info *next_frame)
+{
+ /* On some targets, signal trampolines may have unwind information.
+ We need to recognize them so that we set the frame type correctly
+ (to SIGNAL_FRAME). */
+
+ if (dwarf2_frame_signal_frame_p (get_frame_arch (next_frame),
+ next_frame)
+ && dwarf2_frame_sniffer (next_frame) != NULL)
+ return &dwarf2_signal_frame_unwind;
+
+ return NULL;
+}
\f

/* There is no explicitly defined relationship between the CFA and the
Index: gdb/dwarf2-frame.h
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2-frame.h,v
retrieving revision 1.6
diff -u -p -r1.6 dwarf2-frame.h
--- gdb/dwarf2-frame.h 28 Feb 2004 16:59:32 -0000 1.6
+++ gdb/dwarf2-frame.h 5 Nov 2004 20:22:36 -0000
@@ -79,12 +79,26 @@ extern void dwarf2_frame_set_init_reg (s
void (*init_reg) (struct gdbarch *, int,
struct dwarf2_frame_state_reg *));

+/* Set the architecture-specific signal trampoline recognition
+ function for GDBARCH to SIGNAL_FRAME_P. */
+
+extern void
+ dwarf2_frame_set_signal_frame_p (struct gdbarch *gdbarch,
+ int (*signal_frame_p) (struct gdbarch *,
+ struct frame_info *));
+
/* Return the frame unwind methods for the function that contains PC,
or NULL if it can't be handled by DWARF CFI frame unwinder. */

extern const struct frame_unwind *
dwarf2_frame_sniffer (struct frame_info *next_frame);

+/* Return the frame unwind methods for the function that contains PC,
+ or NULL if it is not a signal frame with DWARF CFI. */
+
+extern const struct frame_unwind *
+ dwarf2_signal_frame_sniffer (struct frame_info *next_frame);
+
/* Return the frame base methods for the function that contains PC, or
NULL if it can't be handled by the DWARF CFI frame unwinder. */

Index: gdb/i386-linux-tdep.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/i386-linux-tdep.c,v
retrieving revision 1.41
diff -u -p -r1.41 i386-linux-tdep.c
--- gdb/i386-linux-tdep.c 6 Aug 2004 20:58:28 -0000 1.41
+++ gdb/i386-linux-tdep.c 5 Nov 2004 20:24:29 -0000
@@ -244,6 +244,27 @@ i386_linux_sigtramp_p (struct frame_info
|| strcmp ("__restore_rt", name) == 0);
}

+/* Return one if the unwound PC from NEXT_FRAME is in a signal trampoline
+ which may have DWARF-2 CFI. */
+
+static int
+i386_linux_dwarf_signal_frame_p (struct gdbarch *gdbarch,
+ struct frame_info *next_frame)
+{
+ CORE_ADDR pc = frame_pc_unwind (next_frame);
+ char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+
+ /* If a vsyscall DSO is in use, the signal trampolines may have these
+ names. */
+ if (name && (strcmp (name, "__kernel_sigreturn") == 0
+ || strcmp (name, "__kernel_rt_sigreturn") == 0))
+ return 1;
+
+ return 0;
+}
+
/* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>. */
#define I386_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 20

@@ -414,6 +435,8 @@ i386_linux_init_abi (struct gdbarch_info

/* GNU/Linux uses the dynamic linker included in the GNU C Library. */
set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+ dwarf2_frame_set_signal_frame_p (gdbarch, i386_linux_dwarf_signal_frame_p);
}

/* Provide a prototype to silence -Wmissing-prototypes. */
Index: gdb/i386-tdep.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/i386-tdep.c,v
retrieving revision 1.203
diff -u -p -r1.203 i386-tdep.c
--- gdb/i386-tdep.c 31 Oct 2004 19:52:46 -0000 1.203
+++ gdb/i386-tdep.c 5 Nov 2004 20:20:46 -0000
@@ -2223,6 +2223,10 @@ i386_gdbarch_init (struct gdbarch_info i
/* Helper for function argument information. */
set_gdbarch_fetch_pointer_argument (gdbarch, i386_fetch_pointer_argument);

+ /* The signal handler might have dwarf2 CFI, via the GNU/Linux
+ vsyscall DSO, so check for this first. */
+ frame_unwind_append_sniffer (gdbarch, dwarf2_signal_frame_sniffer);
+
/* Hook in the DWARF CFI frame unwinder. */
frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);

Index: gdb/inf-ptrace.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/inf-ptrace.c,v
retrieving revision 1.11
diff -u -p -r1.11 inf-ptrace.c
--- gdb/inf-ptrace.c 15 Oct 2004 13:29:33 -0000 1.11
+++ gdb/inf-ptrace.c 5 Nov 2004 20:10:05 -0000
@@ -220,6 +220,10 @@ inf_ptrace_attach (char *args, int from_

inferior_ptid = pid_to_ptid (pid);
push_target (ptrace_ops_hack);
+
+ /* Do this first, before anything has had a chance to query the
+ inferior's symbol table or similar. */
+ observer_notify_inferior_created (&current_target, from_tty);
}

static void
Index: gdb/inftarg.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/inftarg.c,v
retrieving revision 1.34
diff -u -p -r1.34 inftarg.c
--- gdb/inftarg.c 8 Oct 2004 20:29:47 -0000 1.34
+++ gdb/inftarg.c 5 Nov 2004 20:10:05 -0000
@@ -211,6 +211,10 @@ child_attach (char *args, int from_tty)

inferior_ptid = pid_to_ptid (pid);
push_target (&deprecated_child_ops);
+
+ /* Do this first, before anything has had a chance to query the
+ inferior's symbol table or similar. */
+ observer_notify_inferior_created (&current_target, from_tty);
}

#if !defined(CHILD_POST_ATTACH)
Index: gdb/symfile-mem.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/symfile-mem.c,v
retrieving revision 1.3
diff -u -p -r1.3 symfile-mem.c
--- gdb/symfile-mem.c 17 Jul 2004 14:24:07 -0000 1.3
+++ gdb/symfile-mem.c 5 Nov 2004 20:10:05 -0000
@@ -52,13 +52,19 @@
#include "target.h"
#include "value.h"
#include "symfile.h"
+#include "observer.h"
+#include "auxv.h"
+#include "elf/common.h"

/* Read inferior memory at ADDR to find the header of a loaded object file
and read its in-core symbols out of inferior memory. TEMPL is a bfd
- representing the target's format. */
+ representing the target's format. NAME is the name to use for this
+ symbol file in messages; it can be NULL or a malloc-allocated string
+ which will be attached to the BFD. */
static struct objfile *
-symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, int from_tty)
+symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
+ int from_tty)
{
struct objfile *objf;
struct bfd *nbfd;
@@ -75,7 +81,10 @@ symbol_file_add_from_memory (struct bfd
if (nbfd == NULL)
error ("Failed to read a valid object file image from memory.");

- nbfd->filename = xstrdup ("shared object read from target memory");
+ if (name == NULL)
+ nbfd->filename = xstrdup ("shared object read from target memory");
+ else
+ nbfd->filename = name;

if (!bfd_check_format (nbfd, bfd_object))
{
@@ -129,7 +138,73 @@ add_symbol_file_from_memory_command (cha
error ("\
Must use symbol-file or exec-file before add-symbol-file-from-memory.");

- symbol_file_add_from_memory (templ, addr, from_tty);
+ symbol_file_add_from_memory (templ, addr, NULL, from_tty);
+}
+
+/* Arguments for symbol_file_add_from_memory_wrapper. */
+
+struct symbol_file_add_from_memory_args
+{
+ struct bfd *bfd;
+ CORE_ADDR sysinfo_ehdr;
+ char *name;
+ int from_tty;
+};
+
+/* Wrapper function for symbol_file_add_from_memory, for
+ catch_exceptions. */
+
+static int
+symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data)
+{
+ struct symbol_file_add_from_memory_args *args = data;
+
+ symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->name,
+ args->from_tty);
+ return 0;
+}
+
+/* Try to add the symbols for the vsyscall page, if there is one. This function
+ is called via the inferior_created observer. */
+
+static void
+add_vsyscall_page (struct target_ops *target, int from_tty)
+{
+ CORE_ADDR sysinfo_ehdr;
+
+ if (target_auxv_search (target, AT_SYSINFO_EHDR, &sysinfo_ehdr) > 0
+ && sysinfo_ehdr != (CORE_ADDR) 0)
+ {
+ struct bfd *bfd;
+ struct symbol_file_add_from_memory_args args;
+
+ if (core_bfd != NULL)
+ bfd = core_bfd;
+ else if (exec_bfd != NULL)
+ bfd = exec_bfd;
+ else
+ /* FIXME: cagney/2004-05-06: Should not require an existing
+ BFD when trying to create a run-time BFD of the VSYSCALL
+ page in the inferior. Unfortunately that's the current
+ interface so for the moment bail. Introducing a
+ ``bfd_runtime'' (a BFD created using the loaded image) file
+ format should fix this. */
+ {
+ warning ("could not load vsyscall page because no executable was specified");
+ warning ("try using the \"file\" command first");
+ return;
+ }
+ args.bfd = bfd;
+ args.sysinfo_ehdr = sysinfo_ehdr;
+ xasprintf (&args.name, "system-supplied DSO at 0x%s",
+ paddr_nz (sysinfo_ehdr));
+ /* Pass zero for FROM_TTY, because the action of loading the
+ vsyscall DSO was not triggered by the user, even if the user
+ typed "run" at the TTY. */
+ args.from_tty = 0;
+ catch_exceptions (uiout, symbol_file_add_from_memory_wrapper,
+ &args, NULL, RETURN_MASK_ALL);
+ }
}

\f
@@ -143,4 +218,7 @@ Load the symbols out of memory from a dy
Give an expression for the address of the file's shared object file header.",
&cmdlist);

+ /* Want to know of each new inferior so that it's vsyscall info can
+ be extracted. */
+ observer_attach_inferior_created (add_vsyscall_page);
}