Re: gdb has outdated knowledge of signal trampolines

From: Konstantin Belousov <kostikbel_at_gmail.com>
Date: Mon, 25 Nov 2013 19:35:27 +0200
On Mon, Nov 25, 2013 at 12:13:53PM +0200, Andriy Gapon wrote:
> 
> It seems that placement of signal trampolines was changed a while ago.  Possibly
> with the introduction of the shared page, but I am not sure.
> Unfortunately, neither the gdb in base nor the ports gdb were updated to account
> for the new location.
> 
> And thus, for example:
> (kgdb) bt
> #0  thr_kill () at thr_kill.S:3
> #1  0x00000008032c89a7 in nsProfileLock::FatalSignalHandler (signo=6,
> info=<optimized out>, context=0x7ffffb197630)
>     at
> /usr/obj/ports/usr/ports/www/firefox/work/mozilla-release/obj-x86_64-unknown-freebsd11.0/toolkit/profile/nsProfileLock.cpp:180
> #2  0x0000000800f90596 in handle_signal (actp=<optimized out>, sig=6,
> info=0x7ffffb1979a0, ucp=0x7ffffb197630) at /usr/src/lib/libthr/thread/thr_sig.c:237
> #3  0x0000000800f9013f in thr_sighandler (sig=6, info=0x0, _ucp=0x7ffffb197630)
> at /usr/src/lib/libthr/thread/thr_sig.c:182
> #4  0x00007ffffffff003 in ?? ()
> #5  0x0000000800f90010 in ?? () at /usr/src/lib/libthr/thread/thr_sig.c:566 from
> /lib/libthr.so.3
> #6  0x0000000000000000 in ?? ()
> 
> Obviously, the gdb is confused after the frame that has 0x00007ffffffff003.
> 
> I looked only at amd64 code, but I believe that other platforms (all of them?)
> are affected as well.
> 
> The following proof of concept patch for the base gdb seems to fix the case of
> native debugging on amd64 (target case was not tested).
> 
> diff --git a/contrib/gdb/gdb/amd64fbsd-nat.c b/contrib/gdb/gdb/amd64fbsd-nat.c
> index f083734..d49dc45 100644
> --- a/contrib/gdb/gdb/amd64fbsd-nat.c
> +++ b/contrib/gdb/gdb/amd64fbsd-nat.c
> _at__at_ -212,24 +212,23 _at__at_ Please report this to <bug-gdb_at_gnu.org>.",
> 
>    SC_RBP_OFFSET = offset;
> 
> -  /* FreeBSD provides a kern.ps_strings sysctl that we can use to
> +  /* FreeBSD provides a kern.usrstack sysctl that we can use to
>       locate the sigtramp.  That way we can still recognize a sigtramp
>       if its location is changed in a new kernel.  Of course this is
>       still based on the assumption that the sigtramp is placed
> -     directly under the location where the program arguments and
> -     environment can be found.  */
> +     directly at usrstack.  */
>    {
>      int mib[2];
> -    long ps_strings;
> +    long usrstack;
>      size_t len;
> 
>      mib[0] = CTL_KERN;
> -    mib[1] = KERN_PS_STRINGS;
> -    len = sizeof (ps_strings);
> -    if (sysctl (mib, 2, &ps_strings, &len, NULL, 0) == 0)
> +    mib[1] = KERN_USRSTACK;
> +    len = sizeof (usrstack);
> +    if (sysctl (mib, 2, &usrstack, &len, NULL, 0) == 0)
>        {
> -	amd64fbsd_sigtramp_start_addr = ps_strings - 32;
> -	amd64fbsd_sigtramp_end_addr = ps_strings;
> +	amd64fbsd_sigtramp_start_addr = usrstack;
> +	amd64fbsd_sigtramp_end_addr = usrstack + 0x20;
>        }
>    }
>  }
> diff --git a/contrib/gdb/gdb/amd64fbsd-tdep.c b/contrib/gdb/gdb/amd64fbsd-tdep.c
> index e4e02ab..87c1484 100644
> --- a/contrib/gdb/gdb/amd64fbsd-tdep.c
> +++ b/contrib/gdb/gdb/amd64fbsd-tdep.c
> _at__at_ -86,8 +86,8 _at__at_ static int amd64fbsd_r_reg_offset[] =
>  };
> 
>  /* Location of the signal trampoline.  */
> -CORE_ADDR amd64fbsd_sigtramp_start_addr = 0x7fffffffffc0;
> -CORE_ADDR amd64fbsd_sigtramp_end_addr = 0x7fffffffffe0;
> +CORE_ADDR amd64fbsd_sigtramp_start_addr = 0x7ffffffff000;
> +CORE_ADDR amd64fbsd_sigtramp_end_addr = 0x7ffffffff020;
> 
>  /* From <machine/signal.h>.  */
>  int amd64fbsd_sc_reg_offset[] =
> 

Yes, your observation is correct, but the patch could be improved.
Specifically, the location of the signal trampoline code which you
hard-coded into the patch is not guaranteed to be stable, and in fact
somewhat depends on the order of ABI sysvecs registration.  The size
of the signal trampoline is not fixed as well.

Real solution is to start provide vdso for signal trampolines, which
is probably the only real reason to have vdso at all. But this is
labor-intensive and I am not convinced that the ABI changes are worth it
right now.

Instead, I propose the following sysctl helper which should provide the
same 'hackish' way to get the trampoline location, which would work
both for 64bit and 32bit, for later on i386 and amd64. For core files,
this is as bad as before since we cannot guarantee stability of the
trampoline location.

Could you update your gdb patch to use the KERN_PROC_SIGTRAMP from
the patch below ? If this works out, I will add initialization of
sv_szsigcode for ABIs which do not use shared page.

Thank you for looking into this.

 sys/compat/freebsd32/freebsd32.h |  6 +++++
 sys/kern/kern_proc.c             | 58 ++++++++++++++++++++++++++++++++++++++++
 sys/sys/sysctl.h                 |  1 +
 sys/sys/user.h                   |  6 +++++
 4 files changed, 71 insertions(+)

diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h
index 8376e95..94f886e 100644
--- a/sys/compat/freebsd32/freebsd32.h
+++ b/sys/compat/freebsd32/freebsd32.h
_at__at_ -362,6 +362,12 _at__at_ struct kinfo_proc32 {
 	int	ki_tdflags;
 };
 
+struct kinfo_sigtramp32 {
+	uint32_t ksigtramp_start;
+	uint32_t ksigtramp_end;
+	uint32_t ksigtramp_spare[4];
+};
+
 struct kld32_file_stat_1 {
 	int	version;	/* set to sizeof(struct kld_file_stat_1) */
 	char	name[MAXPATHLEN];
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index 9968e76..2e6bc32 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
_at__at_ -2632,6 +2632,60 _at__at_ errout:
 	return (error);
 }
 
+static int
+sysctl_kern_proc_sigtramp(SYSCTL_HANDLER_ARGS)
+{
+	int *name = (int *)arg1;
+	u_int namelen = arg2;
+	struct proc *p;
+	struct kinfo_sigtramp kst;
+	const struct sysentvec *sv;
+	int error;
+#ifdef COMPAT_FREEBSD32
+	struct kinfo_sigtramp32 kst32;
+#endif
+
+	if (namelen != 1)
+		return (EINVAL);
+
+	error = pget((pid_t)name[0], PGET_CANDEBUG, &p);
+	if (error != 0)
+		return (error);
+	sv = p->p_sysent;
+#ifdef COMPAT_FREEBSD32
+	if ((req->flags & SCTL_MASK32) != 0) {
+		bzero(&kst32, sizeof(kst32));
+		if (SV_PROC_FLAG(p, SV_ILP32)) {
+			if (sv->sv_sigcode_base != 0) {
+				kst32.ksigtramp_start = sv->sv_sigcode_base;
+				kst32.ksigtramp_end = sv->sv_sigcode_base +
+				    *sv->sv_szsigcode;
+			} else {
+				kst32.ksigtramp_start = sv->sv_psstrings -
+				    *sv->sv_szsigcode;
+				kst32.ksigtramp_end = sv->sv_psstrings;
+			}
+		}
+		PROC_UNLOCK(p);
+		error = SYSCTL_OUT(req, &kst32, sizeof(kst32));
+		return (error);
+	}
+#endif
+	bzero(&kst, sizeof(kst));
+	if (sv->sv_sigcode_base != 0) {
+		kst.ksigtramp_start = (char *)sv->sv_sigcode_base;
+		kst.ksigtramp_end = (char *)sv->sv_sigcode_base +
+		    *sv->sv_szsigcode;
+	} else {
+		kst.ksigtramp_start = (char *)sv->sv_psstrings -
+		    *sv->sv_szsigcode;
+		kst.ksigtramp_end = (char *)sv->sv_psstrings;
+	}
+	PROC_UNLOCK(p);
+	error = SYSCTL_OUT(req, &kst, sizeof(kst));
+	return (error);
+}
+
 SYSCTL_NODE(_kern, KERN_PROC, proc, CTLFLAG_RD,  0, "Process table");
 
 SYSCTL_PROC(_kern_proc, KERN_PROC_ALL, all, CTLFLAG_RD|CTLTYPE_STRUCT|
_at__at_ -2740,3 +2794,7 _at__at_ static SYSCTL_NODE(_kern_proc, KERN_PROC_UMASK, umask, CTLFLAG_RD |
 static SYSCTL_NODE(_kern_proc, KERN_PROC_OSREL, osrel, CTLFLAG_RW |
 	CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, sysctl_kern_proc_osrel,
 	"Process binary osreldate");
+
+static SYSCTL_NODE(_kern_proc, KERN_PROC_SIGTRAMP, sigtramp, CTLFLAG_RD |
+	CTLFLAG_MPSAFE, sysctl_kern_proc_sigtramp,
+	"Process signal trampoline location");
diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h
index 64292ba..8e70a12 100644
--- a/sys/sys/sysctl.h
+++ b/sys/sys/sysctl.h
_at__at_ -530,6 +530,7 _at__at_ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; );
 #define	KERN_PROC_PS_STRINGS	38	/* get ps_strings location */
 #define	KERN_PROC_UMASK		39	/* process umask */
 #define	KERN_PROC_OSREL		40	/* osreldate for process binary */
+#define	KERN_PROC_SIGTRAMP	41	/* signal trampoline location */
 
 /*
  * KERN_IPC identifiers
diff --git a/sys/sys/user.h b/sys/sys/user.h
index d2e2b6e..e926fe8 100644
--- a/sys/sys/user.h
+++ b/sys/sys/user.h
_at__at_ -498,6 +498,12 _at__at_ struct kinfo_kstack {
 	int	 _kkst_ispare[16];		/* Space for more stuff. */
 };
 
+struct kinfo_sigtramp {
+	void	*ksigtramp_start;
+	void	*ksigtramp_end;
+	void	*ksigtramp_spare[4];
+};
+
 #ifdef _KERNEL
 /* Flags for kern_proc_out function. */
 #define KERN_PROC_NOTHREADS	0x1

Received on Mon Nov 25 2013 - 16:35:40 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:44 UTC