1 /*
2  * Copyright 2008 Red Hat, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #ifdef HAVE_DIX_CONFIG_H
25 #include <dix-config.h>
26 #endif
27 
28 #include "os.h"
29 #include "misc.h"
30 #include <errno.h>
31 #include <string.h>
32 
33 #ifdef HAVE_LIBUNWIND
34 
35 #define UNW_LOCAL_ONLY
36 #include <libunwind.h>
37 
38 #ifndef _GNU_SOURCE
39 #define _GNU_SOURCE
40 #endif
41 #include <dlfcn.h>
42 
43 void
xorg_backtrace(void)44 xorg_backtrace(void)
45 {
46     unw_cursor_t cursor;
47     unw_context_t context;
48     unw_word_t ip;
49     unw_word_t off;
50     unw_proc_info_t pip;
51     int ret, i = 0;
52     char procname[256];
53     const char *filename;
54     Dl_info dlinfo;
55 
56     pip.unwind_info = NULL;
57     ret = unw_getcontext(&context);
58     if (ret) {
59         ErrorFSigSafe("unw_getcontext failed: %s [%d]\n", unw_strerror(ret),
60                 ret);
61         return;
62     }
63 
64     ret = unw_init_local(&cursor, &context);
65     if (ret) {
66         ErrorFSigSafe("unw_init_local failed: %s [%d]\n", unw_strerror(ret),
67                 ret);
68         return;
69     }
70 
71     ErrorFSigSafe("\n");
72     ErrorFSigSafe("Backtrace:\n");
73     ret = unw_step(&cursor);
74     while (ret > 0) {
75         ret = unw_get_proc_info(&cursor, &pip);
76         if (ret) {
77             ErrorFSigSafe("unw_get_proc_info failed: %s [%d]\n",
78                     unw_strerror(ret), ret);
79             break;
80         }
81 
82         off = 0;
83         ret = unw_get_proc_name(&cursor, procname, 256, &off);
84         if (ret && ret != -UNW_ENOMEM) {
85             if (ret != -UNW_EUNSPEC)
86                 ErrorFSigSafe("unw_get_proc_name failed: %s [%d]\n",
87                         unw_strerror(ret), ret);
88             procname[0] = '?';
89             procname[1] = 0;
90         }
91 
92         if (unw_get_reg (&cursor, UNW_REG_IP, &ip) < 0)
93           ip = pip.start_ip + off;
94         if (dladdr((void *)(uintptr_t)(ip), &dlinfo) && dlinfo.dli_fname &&
95                 *dlinfo.dli_fname)
96             filename = dlinfo.dli_fname;
97         else
98             filename = "?";
99 
100         ErrorFSigSafe("%u: %s (%s%s+0x%x) [%p]\n", i++, filename, procname,
101             ret == -UNW_ENOMEM ? "..." : "", (int)off,
102             (void *)(uintptr_t)(ip));
103 
104         ret = unw_step(&cursor);
105         if (ret < 0)
106             ErrorFSigSafe("unw_step failed: %s [%d]\n", unw_strerror(ret), ret);
107     }
108     ErrorFSigSafe("\n");
109 }
110 #else /* HAVE_LIBUNWIND */
111 #ifdef HAVE_BACKTRACE
112 #ifndef _GNU_SOURCE
113 #define _GNU_SOURCE
114 #endif
115 #include <dlfcn.h>
116 #include <execinfo.h>
117 
118 void
xorg_backtrace(void)119 xorg_backtrace(void)
120 {
121     const int BT_SIZE = 64;
122     void *array[BT_SIZE];
123     const char *mod;
124     int size, i;
125     Dl_info info;
126 
127     ErrorFSigSafe("\n");
128     ErrorFSigSafe("Backtrace:\n");
129     size = backtrace(array, BT_SIZE);
130     for (i = 0; i < size; i++) {
131         int rc = dladdr(array[i], &info);
132 
133         if (rc == 0) {
134             ErrorFSigSafe("%u: ?? [%p]\n", i, array[i]);
135             continue;
136         }
137         mod = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)";
138         if (info.dli_saddr)
139             ErrorFSigSafe(
140                 "%u: %s (%s+0x%x) [%p]\n",
141                 i,
142                 mod,
143                 info.dli_sname,
144                 (unsigned int)((char *) array[i] -
145                                (char *) info.dli_saddr),
146                 array[i]);
147         else
148             ErrorFSigSafe(
149                 "%u: %s (%p+0x%x) [%p]\n",
150                 i,
151                 mod,
152                 info.dli_fbase,
153                 (unsigned int)((char *) array[i] -
154                                (char *) info.dli_fbase),
155                 array[i]);
156     }
157     ErrorFSigSafe("\n");
158 }
159 
160 #else                           /* not glibc or glibc < 2.1 */
161 
162 #if defined(__sun) && defined(__SVR4)
163 #define HAVE_PSTACK
164 #endif
165 
166 #if defined(HAVE_WALKCONTEXT)   /* Solaris 9 & later */
167 
168 #include <ucontext.h>
169 #include <signal.h>
170 #include <dlfcn.h>
171 #include <sys/elf.h>
172 
173 #ifdef _LP64
174 #define ElfSym Elf64_Sym
175 #else
176 #define ElfSym Elf32_Sym
177 #endif
178 
179 /* Called for each frame on the stack to print its contents */
180 static int
xorg_backtrace_frame(uintptr_t pc,int signo,void * arg)181 xorg_backtrace_frame(uintptr_t pc, int signo, void *arg)
182 {
183     Dl_info dlinfo;
184     ElfSym *dlsym;
185     char header[32];
186     int depth = *((int *) arg);
187 
188     if (signo) {
189         char signame[SIG2STR_MAX];
190 
191         if (sig2str(signo, signame) != 0) {
192             strcpy(signame, "unknown");
193         }
194 
195         ErrorFSigSafe("** Signal %u (%s)\n", signo, signame);
196     }
197 
198     snprintf(header, sizeof(header), "%d: 0x%lx", depth, pc);
199     *((int *) arg) = depth + 1;
200 
201     /* Ask system dynamic loader for info on the address */
202     if (dladdr1((void *) pc, &dlinfo, (void **) &dlsym, RTLD_DL_SYMENT)) {
203         unsigned long offset = pc - (uintptr_t) dlinfo.dli_saddr;
204         const char *symname;
205 
206         if (offset < dlsym->st_size) {  /* inside a function */
207             symname = dlinfo.dli_sname;
208         }
209         else {                  /* found which file it was in, but not which function */
210             symname = "<section start>";
211             offset = pc - (uintptr_t) dlinfo.dli_fbase;
212         }
213         ErrorFSigSafe("%s: %s:%s+0x%x\n", header, dlinfo.dli_fname, symname,
214                      offset);
215 
216     }
217     else {
218         /* Couldn't find symbol info from system dynamic loader, should
219          * probably poke elfloader here, but haven't written that code yet,
220          * so we just print the pc.
221          */
222         ErrorFSigSafe("%s\n", header);
223     }
224 
225     return 0;
226 }
227 #endif                          /* HAVE_WALKCONTEXT */
228 
229 #ifdef HAVE_PSTACK
230 #include <unistd.h>
231 
232 static int
xorg_backtrace_pstack(void)233 xorg_backtrace_pstack(void)
234 {
235     pid_t kidpid;
236     int pipefd[2];
237 
238     if (pipe(pipefd) != 0) {
239         return -1;
240     }
241 
242     kidpid = fork1();
243 
244     if (kidpid == -1) {
245         /* ERROR */
246         return -1;
247     }
248     else if (kidpid == 0) {
249         /* CHILD */
250         char parent[16];
251 
252         seteuid(0);
253         close(STDIN_FILENO);
254         close(STDOUT_FILENO);
255         dup2(pipefd[1], STDOUT_FILENO);
256         closefrom(STDERR_FILENO);
257 
258         snprintf(parent, sizeof(parent), "%d", getppid());
259         execle("/usr/bin/pstack", "pstack", parent, NULL);
260         exit(1);
261     }
262     else {
263         /* PARENT */
264         char btline[256];
265         int kidstat;
266         int bytesread;
267         int done = 0;
268 
269         close(pipefd[1]);
270 
271         while (!done) {
272             bytesread = read(pipefd[0], btline, sizeof(btline) - 1);
273 
274             if (bytesread > 0) {
275                 btline[bytesread] = 0;
276                 ErrorFSigSafe("%s", btline);
277             }
278             else if ((bytesread < 0) || ((errno != EINTR) && (errno != EAGAIN)))
279                 done = 1;
280         }
281         close(pipefd[0]);
282         waitpid(kidpid, &kidstat, 0);
283         if (kidstat != 0)
284             return -1;
285     }
286     return 0;
287 }
288 #endif                          /* HAVE_PSTACK */
289 
290 #if defined(HAVE_PSTACK) || defined(HAVE_WALKCONTEXT)
291 
292 void
xorg_backtrace(void)293 xorg_backtrace(void)
294 {
295 
296     ErrorFSigSafe("\n");
297     ErrorFSigSafe("Backtrace:\n");
298 
299 #ifdef HAVE_PSTACK
300 /* First try fork/exec of pstack - otherwise fall back to walkcontext
301    pstack is preferred since it can print names of non-exported functions */
302 
303     if (xorg_backtrace_pstack() < 0)
304 #endif
305     {
306 #ifdef HAVE_WALKCONTEXT
307         ucontext_t u;
308         int depth = 1;
309 
310         if (getcontext(&u) == 0)
311             walkcontext(&u, xorg_backtrace_frame, &depth);
312         else
313 #endif
314             ErrorFSigSafe("Failed to get backtrace info: %s\n", strerror(errno));
315     }
316     ErrorFSigSafe("\n");
317 }
318 
319 #else
320 
321 /* Default fallback if we can't find any way to get a backtrace */
322 void
xorg_backtrace(void)323 xorg_backtrace(void)
324 {
325     return;
326 }
327 
328 #endif
329 #endif
330 #endif
331