# DP: - Enable support for symbolic tracebacks in exceptions (delete the dummy # DP: convert_addresses from adaint.c, and provide a real one separately.) Ported Jürgen Pfeifer's patch to enable symbolic tracebacks on Debian GNU/Linux. The binary distribution of GNAT 3.15p comes with an old version of binutils that includes a library, libaddr2line.a. This library does not exist in recent versions of binutils. The patch works around this by calling /usr/bin/addr2line (still part of binutils) and parsing the output. See debian/convert_addresses.c for the gory details. I have modified convert_addresses.c to not use a shell script anymore; Debian controls the version of binutils which is installed. Also, I use execve instead of execle. -- Ludovic Brenta. # ' make emacs highlighting happy Index: b/src/gcc/ada/gcc-interface/Makefile.in =================================================================== --- a/src/gcc/ada/gcc-interface/Makefile.in +++ b/src/gcc/ada/gcc-interface/Makefile.in @@ -270,7 +270,8 @@ # Both . and srcdir are used, in that order, # so that tm.h and config.h will be found in the compilation # subdirectory rather than in the source directory. -INCLUDES = -I- -I. -I.. -I$(srcdir)/ada -I$(srcdir) -I$(ftop_srcdir)/include $(GMPINC) +INCLUDES = -iquote . -iquote .. -iquote $(srcdir)/ada -iquote $(srcdir) \ + -iquote $(ftop_srcdir)/include $(GMPINC) ADA_INCLUDES = -I- -I. -I$(srcdir)/ada @@ -2426,7 +2427,7 @@ # library. LIBGNAT_OBJS is the list of object files for libgnat. # thread.c is special as put into GNATRTL_TASKING_OBJS by Makefile.rtl LIBGNAT_OBJS = adadecode.o adaint.o argv.o aux-io.o \ - cal.o cio.o cstreams.o ctrl_c.o \ + cal.o cio.o convert_addresses.o cstreams.o ctrl_c.o \ env.o errno.o exit.o expect.o final.o \ init.o initialize.o locales.o mkdir.o \ raise.o seh_init.o socket.o sysdep.o \ @@ -3104,6 +3105,7 @@ socket.o : socket.c gsocket.h sysdep.o : sysdep.c raise.o : raise.c raise.h +convert_addresses.o : convert_addresses.c sigtramp-armdroid.o : sigtramp-armdroid.c sigtramp.h sigtramp-armvxw.o : sigtramp-armvxw.c sigtramp.h sigtramp-ppcvxw.o : sigtramp-ppcvxw.c sigtramp.h Index: b/src/gcc/ada/adaint.c =================================================================== --- a/src/gcc/ada/adaint.c +++ b/src/gcc/ada/adaint.c @@ -3608,35 +3608,6 @@ } #endif -#if defined (IS_CROSS) \ - || (! ((defined (sparc) || defined (i386)) && defined (sun) \ - && defined (__SVR4)) \ - && ! (defined (linux) && (defined (i386) || defined (__x86_64__))) \ - && ! (defined (linux) && defined (__ia64__)) \ - && ! (defined (linux) && defined (powerpc)) \ - && ! defined (__FreeBSD__) \ - && ! defined (__Lynx__) \ - && ! defined (__hpux__) \ - && ! defined (__APPLE__) \ - && ! defined (_AIX) \ - && ! defined (VMS) \ - && ! defined (__MINGW32__)) - -/* Dummy function to satisfy g-trasym.o. See the preprocessor conditional - just above for a list of native platforms that provide a non-dummy - version of this procedure in libaddr2line.a. */ - -void -convert_addresses (const char *file_name ATTRIBUTE_UNUSED, - void *addrs ATTRIBUTE_UNUSED, - int n_addr ATTRIBUTE_UNUSED, - void *buf ATTRIBUTE_UNUSED, - int *len ATTRIBUTE_UNUSED) -{ - *len = 0; -} -#endif - #if defined (_WIN32) int __gnat_argument_needs_quote = 1; #else Index: b/src/gcc/ada/convert_addresses.c =================================================================== --- /dev/null +++ b/src/gcc/ada/convert_addresses.c @@ -0,0 +1,159 @@ +/* + Copyright (C) 1999 by Juergen Pfeifer + Ada for Linux Team (ALT) + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, distribute with modifications, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name(s) of the above copyright + holders shall not be used in advertising or otherwise to promote the + sale, use or other dealings in this Software without prior written + authorization. +*/ +#include +#include +#include +#include +#include +#include + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define MAX_LINE 1024 + +#define CLOSE1 close(fd1[0]); close(fd1[1]) +#define CLOSE2 close(fd2[0]); close(fd2[1]) +#define RESTSIG sigaction(SIGPIPE,&oact,NULL) + +void convert_addresses +(const char const *file_name, + void* addrs[], + int n_addr, + char* buf, + int* len) +{ + int max_len = *len; + pid_t child; + + struct sigaction act, oact; + + int fd1[2], fd2[2]; + + if (!file_name) { + return; + } + + *buf = 0; *len = 0; + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGPIPE,&act,&oact) < 0) + return; + + if (pipe(fd1) >= 0) { + if (pipe(fd2)>=0) { + if ((child = fork()) < 0) { + CLOSE1; CLOSE2; RESTSIG; + return; + } + else { + if (0==child) { + close(fd1[1]); + close(fd2[0]); + if (fd1[0] != STDIN_FILENO) { + if (dup2(fd1[0],STDIN_FILENO) != STDIN_FILENO) { + CLOSE1; CLOSE2; + } + close(fd1[0]); + } + if (fd2[1] != STDOUT_FILENO) { + if (dup2(fd2[1],STDOUT_FILENO) != STDOUT_FILENO) { + CLOSE1; CLOSE2; + } + close(fd2[1]); + } + { + /* As pointed out by Florian Weimer to me, it is a + security threat to call the script with a user defined + environment and using the path. That would be Trojans + pleasure. Therefore we use the absolute path to + addr2line and an empty environment. That should be + safe. + */ + char *file_name_for_execve = strdup (file_name); /* non-const */ + char *const argv[] = { "addr2line", + "-e", + file_name_for_execve, + "--demangle=gnat", + "--functions", + "--basenames", + NULL }; + char *const envp[] = { NULL }; + if (execve("/usr/bin/addr2line", argv, envp) < 0) { + CLOSE1; CLOSE2; + } + if (file_name_for_execve) { free (file_name_for_execve); } + } + } + else { + int i, n; + char hex[16]; + char line[MAX_LINE + 1]; + char *p; + char *s = buf; + + /* Parent context */ + close(fd1[0]); + close(fd2[1]); + + for(i=0; i < n_addr; i++) { + snprintf(hex,sizeof(hex),"%p\n",addrs[i]); + write(fd1[1],hex,strlen(hex)); + n = read(fd2[0],line,MAX_LINE); + if (n<=0) + break; + line[n]=0; + /* We have approx. 16 additional chars for "%p in " clause. + We use this info to prevent a buffer overrun. + */ + if (n + 16 + (*len) > max_len) + break; + p = strchr(line,'\n'); + if (p) { + if (*(p+1)) { + *p = 0; + *len += snprintf(s, (max_len - (*len)), "%p in %s at %s",addrs[i], line, p+1); + } + else { + *len += snprintf(s, (max_len - (*len)), "%p at %s",addrs[i], line); + } + s = buf + (*len); + } + } + close(fd1[1]); + close(fd2[0]); + } + } + } + else { + CLOSE1; + } + } + RESTSIG; +} Index: b/src/gcc/ada/g-trasym.adb =================================================================== --- a/src/gcc/ada/g-trasym.adb +++ b/src/gcc/ada/g-trasym.adb @@ -33,40 +33,110 @@ -- is not supported. It returns tracebacks as lists of LF separated strings of -- the form "0x..." corresponding to the addresses. +with System.Soft_Links; with Ada.Exceptions.Traceback; use Ada.Exceptions.Traceback; -with System.Address_Image; package body GNAT.Traceback.Symbolic is + package TSL renames System.Soft_Links; + + -- To perform the raw addresses to symbolic form translation we rely on a + -- libaddr2line symbolizer which examines debug info from a provided + -- executable file name, and an absolute path is needed to ensure the file + -- is always found. This is "__gnat_locate_exec_on_path (gnat_argv [0])" + -- for our executable file, a fairly heavy operation so we cache the + -- result. + + Exename : System.Address; + -- Pointer to the name of the executable file to be used on all + -- invocations of the libaddr2line symbolization service. + + Exename_Resolved : Boolean := False; + -- Flag to indicate whether we have performed the executable file name + -- resolution already. Relying on a not null Exename for this purpose + -- would be potentially inefficient as this is what we will get if the + -- resolution attempt fails. + ------------------------ -- Symbolic_Traceback -- ------------------------ function Symbolic_Traceback (Traceback : Tracebacks_Array) return String is + + procedure convert_addresses + (filename : System.Address; + addrs : System.Address; + n_addrs : Integer; + buf : System.Address; + len : System.Address); + pragma Import (C, convert_addresses, "convert_addresses"); + -- This is the procedure version of the Ada-aware addr2line. It places + -- in BUF a string representing the symbolic translation of the N_ADDRS + -- raw addresses provided in ADDRS, looked up in debug information from + -- FILENAME. LEN points to an integer which contains the size of the + -- BUF buffer at input and the result length at output. + -- + -- Note that this procedure is *not* thread-safe. + + type Argv_Array is array (0 .. 0) of System.Address; + gnat_argv : access Argv_Array; + pragma Import (C, gnat_argv, "gnat_argv"); + + function locate_exec_on_path + (c_exename : System.Address) return System.Address; + pragma Import (C, locate_exec_on_path, "__gnat_locate_exec_on_path"); + + B_Size : constant Integer := 256 * Traceback'Length; + Len : Integer := B_Size; + Res : String (1 .. B_Size); + + use type System.Address; + begin + -- The symbolic translation of an empty set of addresses is an empty + -- string. + if Traceback'Length = 0 then return ""; + end if; - else - declare - Img : String := System.Address_Image (Traceback (Traceback'First)); + -- If our input set of raw addresses is not empty, resort to the + -- libaddr2line service to symbolize it all. - Result : String (1 .. (Img'Length + 3) * Traceback'Length); - Last : Natural := 0; + -- Compute, cache and provide the absolute path to our executable file + -- name as the binary file where the relevant debug information is to be + -- found. If the executable file name resolution fails, we have no + -- sensible basis to invoke the symbolizer at all. + + -- Protect all this against concurrent accesses explicitly, as the + -- underlying services are potentially thread unsafe. + + TSL.Lock_Task.all; + + if not Exename_Resolved then + Exename := locate_exec_on_path (gnat_argv (0)); + Exename_Resolved := True; + end if; + + if Exename /= System.Null_Address then + Len := Res'Length; + convert_addresses + (Exename, Traceback'Address, Traceback'Length, + Res (1)'Address, Len'Address); + end if; + + TSL.Unlock_Task.all; - begin - for J in Traceback'Range loop - Img := System.Address_Image (Traceback (J)); - Result (Last + 1 .. Last + 2) := "0x"; - Last := Last + 2; - Result (Last + 1 .. Last + Img'Length) := Img; - Last := Last + Img'Length + 1; - Result (Last) := ASCII.LF; - end loop; + -- Return what the addr2line symbolizer has produced if we have called + -- it (the executable name resolution succeeded), or an empty string + -- otherwise. - return Result (1 .. Last); - end; + if Exename /= System.Null_Address then + return Res (1 .. Len); + else + return ""; end if; + end Symbolic_Traceback; function Symbolic_Traceback (E : Exception_Occurrence) return String is Index: b/src/gcc/ada/tracebak.c =================================================================== --- a/src/gcc/ada/tracebak.c +++ b/src/gcc/ada/tracebak.c @@ -425,7 +425,7 @@ /* Starting with GCC 4.6, -fomit-frame-pointer is turned on by default for 32-bit x86/Linux as well and DWARF 2 unwind tables are emitted instead. See the x86-64 case below for the drawbacks with this approach. */ -#if defined (linux) && (__GNUC__ * 10 + __GNUC_MINOR__ > 45) +#if (defined (linux) || defined(__GNU__)) && (__GNUC__ * 10 + __GNUC_MINOR__ > 45) #define USE_GCC_UNWINDER #else #define USE_GENERIC_UNWINDER