1 /*
2   Copyright (C) 1999 by Juergen Pfeifer <juergen.pfeifer@gmx.net>
3   Ada for Linux Team (ALT)
4   Heavily modified by John Marino <http://www.dragonlace.net>
5 
6   Permission is hereby granted, free of charge, to any person obtaining a
7   copy of this software and associated documentation files (the
8   "Software"), to deal in the Software without restriction, including
9   without limitation the rights to use, copy, modify, merge, publish,
10   distribute, distribute with modifications, sublicense, and/or sell
11   copies of the Software, and to permit persons to whom the Software is
12   furnished to do so, subject to the following conditions:
13 
14   The above copyright notice and this permission notice shall be included
15   in all copies or substantial portions of the Software.
16 
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20   IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21   DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
23   THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25   Except as contained in this notice, the name(s) of the above copyright
26   holders shall not be used in advertising or otherwise to promote the
27   sale, use or other dealings in this Software without prior written
28   authorization.
29 */
30 
31 #ifdef IS_CROSS
32 
33 
34 /*
35  * Running addr2line doesn't make sense for cross-compiled objects.
36  * Create a dummy function to satisfy g-trasym.o
37  */
38 
39 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)40 convert_addresses (const char *file_name ATTRIBUTE_UNUSED,
41                    void *addrs ATTRIBUTE_UNUSED,
42                    int n_addr ATTRIBUTE_UNUSED,
43                    void *buf ATTRIBUTE_UNUSED,
44                    int *len ATTRIBUTE_UNUSED)
45 {
46   *len = 0;
47 }
48 
49 #else
50 
51 
52 /*
53  * use the external program /usr/bin/addr2line to convert addresses
54  * into file names and line numbers
55  */
56 
57 #include <sys/types.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <signal.h>
62 
63 #define CLOSE_SENDPIPE close(sendpipe[0]); close(sendpipe[1])
64 #define CLOSE_READPIPE close(readpipe[0]); close(readpipe[1])
65 #define DUP2CLOSE(oldfd, newfd) dup2(oldfd, newfd); close(oldfd);
66 #define RESTSIG sigaction(SIGPIPE,&oact,NULL)
67 
68 #define MAX_LINE     1024
69 #define PARENT_READ  readpipe[0]
70 #define CHILD_WRITE  readpipe[1]
71 #define CHILD_READ   sendpipe[0]
72 #define PARENT_WRITE sendpipe[1]
73 
74 #if defined (__sun__)
75 #define ADDR2LINE_PROG        "/usr/gnu/bin/addr2line"
76 #else
77 #define ADDR2LINE_PROG        "/usr/bin/addr2line"
78 #endif
79 
80 void
convert_addresses(const char * file_name,void * addrs,int n_addr,void * buf,int * len)81 convert_addresses (const char *file_name,
82                    void *addrs,
83                    int   n_addr,
84                    void *buf,
85                    int  *len)
86 {
87   int max_len = *len;
88   pid_t childpid;
89 
90   struct sigaction act, oact;
91 
92   int sendpipe[2] = {-1,-1},        /* parent -> child */
93       readpipe[2] = {-1,-1};        /* parent <- child */
94 
95   *len = 0;
96   act.sa_handler = SIG_IGN;
97   sigemptyset(&act.sa_mask);
98   act.sa_flags = 0;
99   if (sigaction(SIGPIPE,&act,&oact) < 0)
100     return;
101 
102   if (pipe(sendpipe) < 0) { RESTSIG; return; }
103   if (pipe(readpipe) < 0) { CLOSE_SENDPIPE; RESTSIG; return; }
104   if ((childpid = fork()) < 0) {
105     CLOSE_READPIPE;
106     CLOSE_SENDPIPE;
107     RESTSIG;
108     return;
109   }
110 
111   if (childpid == 0) {    /* child process */
112     close(PARENT_WRITE);
113     close(PARENT_READ);
114     if ((CHILD_READ != STDIN_FILENO) && (CHILD_WRITE != STDOUT_FILENO)) {
115       if ((CHILD_READ == STDOUT_FILENO) && (CHILD_WRITE == STDIN_FILENO)) {
116         const int temp_fd = dup(CHILD_WRITE);
117         close (CHILD_WRITE);
118         DUP2CLOSE (CHILD_READ, STDIN_FILENO);
119         DUP2CLOSE (temp_fd,    STDOUT_FILENO);
120       }
121       else if ((CHILD_READ == STDIN_FILENO) && (CHILD_WRITE > 1)) {
122         DUP2CLOSE (CHILD_WRITE, STDOUT_FILENO);
123       }
124       else if ((CHILD_READ > 1) && (CHILD_WRITE == STDOUT_FILENO)) {
125         DUP2CLOSE (CHILD_READ, STDIN_FILENO);
126       }
127       else if ((CHILD_READ > 1) && (CHILD_WRITE == STDIN_FILENO)) {
128         DUP2CLOSE (CHILD_WRITE, STDOUT_FILENO);
129         DUP2CLOSE (CHILD_READ,  STDIN_FILENO);
130       }
131       else {
132         /* CHILD_READ >= 1 and CHILD_WRITE > 1 */
133         DUP2CLOSE (CHILD_READ,  STDIN_FILENO);
134         DUP2CLOSE (CHILD_WRITE, STDOUT_FILENO);
135       }
136     }
137     /* As pointed out by Florian Weimer to JP, it is a security threat to call
138        the script with a user defined environment and using the path. That
139        would be Trojans pleasure.  Therefore the absolute path to addr2line
140        and an empty environment is used. That should be safe.
141     */
142     char *const argv[] = { "addr2line",
143                            "-e", file_name,
144                            "--demangle=gnat",
145                            "--functions",
146                            "--basenames",
147                            NULL };
148     char *const envp[] = { NULL };
149     if (execve(ADDR2LINE_PROG, argv, envp) < 0) {
150       close (CHILD_WRITE);
151       close (CHILD_READ);
152       RESTSIG;
153       exit (1);
154     }
155   }
156 
157   /* Below this line is parent process */
158   int i, n;
159   char hex[16];
160   char line[MAX_LINE + 1];
161   char *p;
162   char *s = buf;
163   long *trace_address = addrs;
164 
165   close(CHILD_WRITE);
166   close(CHILD_READ);
167 
168   for(i=0; i < n_addr; i++) {
169     snprintf(hex,sizeof(hex),"%#lx\n",*trace_address);
170     write(PARENT_WRITE,hex,strlen(hex));
171     n = read(PARENT_READ,line,MAX_LINE);
172     if (n<=0)
173       break;
174 
175     line[n]=0;
176     /* We have approx. 16 additional chars for "%#lx in " clause.
177        We use this info to prevent a buffer overrun. */
178     if (n + 16 + (*len) > max_len)
179       break;
180 
181     p = strchr(line,'\n');
182     if (p) {
183       if (*(p+1)) {
184         *p = 0;
185         *len += snprintf(s, (max_len - (*len)), "%#lx in %s at %s",
186                          *trace_address, line, p+1);
187       }
188       else {
189         *len += snprintf(s, (max_len - (*len)), "%#lx at %s",
190                          *trace_address, line);
191       }
192       s = buf + (*len);
193     }
194     trace_address += 1;
195   }
196   close (PARENT_WRITE);
197   close (PARENT_READ);
198   RESTSIG;
199 }
200 
201 #endif
202