Patchwork [libfortran] PR 48931 Async-signal-safety of backtrace signal handler, Part 2

login
register
mail settings
Submitter Janne Blomqvist
Date May 29, 2011, 10:17 a.m.
Message ID <BANLkTikcEgpU5MarN=9AVBEUQ60qW97cTw@mail.gmail.com>
Download mbox | patch
Permalink /patch/97829/
State New
Headers show

Comments

Janne Blomqvist - May 29, 2011, 10:17 a.m.
Hi,

FX reminded me that execvp() is not guaranteed to be
async-signal-safe, hence we must use execve(). So instead during
library initialization try to find addr2line.

Regtested on x86_64-unknown-linux-gnu, Ok for trunk?

2011-05-29  Janne Blomqvist  <jb@gcc.gnu.org>

	PR libfortran/48931
	* libgfortran.h (find_addr2line): New prototype.
	* runtime/backtrace.c (show_backtrace): Use async-signal-safe
	execve and stored path of addr2line.
	* runtime/compile_options.c (maybe_find_addr2line): New function.
	(set_options): Call maybe_find_addr2line if backtracing is enabled.
	* runtime/main.c (find_addr2line): New function.
	(init): Call find_addr2line if backtracing is enabled.
	(cleanup): Free addr2line_path.
Steve Kargl - May 29, 2011, 4:52 p.m.
On Sun, May 29, 2011 at 01:17:05PM +0300, Janne Blomqvist wrote:
> 
> FX reminded me that execvp() is not guaranteed to be
> async-signal-safe, hence we must use execve(). So instead during
> library initialization try to find addr2line.
> 
> Regtested on x86_64-unknown-linux-gnu, Ok for trunk?
> 
> 2011-05-29  Janne Blomqvist  <jb@gcc.gnu.org>
> 
> 	PR libfortran/48931
> 	* libgfortran.h (find_addr2line): New prototype.
> 	* runtime/backtrace.c (show_backtrace): Use async-signal-safe
> 	execve and stored path of addr2line.
> 	* runtime/compile_options.c (maybe_find_addr2line): New function.
> 	(set_options): Call maybe_find_addr2line if backtracing is enabled.
> 	* runtime/main.c (find_addr2line): New function.
> 	(init): Call find_addr2line if backtracing is enabled.
> 	(cleanup): Free addr2line_path.
> 

I suppose I should let FX review the patch, but I know he's
been busy lately.  So, the patch is OK.  Thanks.

Patch

diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h
index e77ba10..b72b250 100644
--- a/libgfortran/libgfortran.h
+++ b/libgfortran/libgfortran.h
@@ -665,6 +665,9 @@  export_proto(store_exe_path);
 extern char * full_exe_path (void);
 internal_proto(full_exe_path);
 
+extern void find_addr2line (void);
+internal_proto(find_addr2line);
+
 /* backtrace.c */
 
 extern void show_backtrace (void);
diff --git a/libgfortran/runtime/backtrace.c b/libgfortran/runtime/backtrace.c
index dff4466..0269300 100644
--- a/libgfortran/runtime/backtrace.c
+++ b/libgfortran/runtime/backtrace.c
@@ -102,6 +102,9 @@  fd_gets (char *s, int size, int fd)
 }
 
 
+extern char *addr2line_path;
+
+
 /* show_backtrace displays the backtrace, currently obtained by means of
    the glibc backtrace* functions.  */
 
@@ -122,6 +125,9 @@  show_backtrace (void)
 
 #if CAN_PIPE
 
+  if (addr2line_path == NULL)
+    goto fallback_noerr;
+
   /* We attempt to extract file and line information from addr2line.  */
   do
   {
@@ -144,6 +150,7 @@  show_backtrace (void)
 	/* Child process.  */
 #define NUM_FIXEDARGS 7
 	char *arg[NUM_FIXEDARGS];
+	char *newenv[] = { NULL };
 
 	close (f[0]);
 
@@ -158,14 +165,14 @@  show_backtrace (void)
 	  _exit (1);
 	close (f[1]);
 
-	arg[0] = (char *) "addr2line";
+	arg[0] = addr2line_path;
 	arg[1] = (char *) "-e";
 	arg[2] = full_exe_path ();
 	arg[3] = (char *) "-f";
 	arg[4] = (char *) "-s";
 	arg[5] = (char *) "-C";
 	arg[6] = NULL;
-	execvp (arg[0], arg);
+	execve (addr2line_path, arg, newenv);
 	_exit (1);
 #undef NUM_FIXEDARGS
       }
@@ -262,6 +269,7 @@  fallback:
 
 #endif /* CAN_PIPE */
 
+fallback_noerr:
   /* Fallback to the glibc backtrace.  */
   estr_write ("\nBacktrace for this error:\n");
   backtrace_symbols_fd (trace, depth, STDERR_FILENO);
diff --git a/libgfortran/runtime/compile_options.c b/libgfortran/runtime/compile_options.c
index dc0da4b..c3e64de 100644
--- a/libgfortran/runtime/compile_options.c
+++ b/libgfortran/runtime/compile_options.c
@@ -58,6 +58,15 @@  backtrace_handler (int signum)
 }
 
 
+/* Helper function for set_options because we need to access the
+   global variable options which is not seen in set_options.  */
+static void
+maybe_find_addr2line (void)
+{
+  if (options.backtrace == -1)
+    find_addr2line ();
+}
+
 /* Set the usual compile-time options.  */
 extern void set_options (int , int []);
 export_proto(set_options);
@@ -131,6 +140,8 @@  set_options (int num, int options[])
 #if defined(SIGXFSZ)
       signal (SIGXFSZ, backtrace_handler);
 #endif
+
+      maybe_find_addr2line ();
     }
 #endif
 
diff --git a/libgfortran/runtime/main.c b/libgfortran/runtime/main.c
index 54d9e09..bc8dab4 100644
--- a/libgfortran/runtime/main.c
+++ b/libgfortran/runtime/main.c
@@ -139,6 +139,40 @@  full_exe_path (void)
 }
 
 
+char *addr2line_path;
+
+/* Find addr2line and store the path.  */
+
+void
+find_addr2line (void)
+{
+#ifdef HAVE_ACCESS
+#define A2L_LEN 10
+  char *path = getenv ("PATH");
+  size_t n = strlen (path);
+  char ap[n + 1 + A2L_LEN];
+  size_t ai = 0;
+  for (size_t i = 0; i < n; i++)
+    {
+      if (path[i] != ':')
+	ap[ai++] = path[i];
+      else
+	{
+	  ap[ai++] = '/';
+	  memcpy (ap + ai, "addr2line", A2L_LEN);
+	  if (access (ap, R_OK|X_OK) == 0)
+	    {
+	      addr2line_path = strdup (ap);
+	      return;
+	    }
+	  else
+	    ai = 0;
+	}
+    }
+#endif
+}
+
+
 /* Set the saved values of the command line arguments.  */
 
 void
@@ -185,6 +219,9 @@  init (void)
   /* if (argc > 1 && strcmp(argv[1], "--resume") == 0) resume();  */
 #endif
 
+  if (options.backtrace == 1)
+    find_addr2line ();
+
   random_seed_i4 (NULL, NULL, NULL);
 }
 
@@ -198,4 +235,6 @@  cleanup (void)
   
   if (please_free_exe_path_when_done)
     free ((char *) exe_path);
+
+  free (addr2line_path);
 }