1999f82afSJohn Marino /* $NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $ */
2999f82afSJohn Marino
3999f82afSJohn Marino /*-
4999f82afSJohn Marino * Copyright (c) 2012 The NetBSD Foundation, Inc.
5999f82afSJohn Marino * All rights reserved.
6999f82afSJohn Marino *
7999f82afSJohn Marino * This code is derived from software contributed to The NetBSD Foundation
8999f82afSJohn Marino * by Christos Zoulas.
9999f82afSJohn Marino *
10999f82afSJohn Marino * Redistribution and use in source and binary forms, with or without
11999f82afSJohn Marino * modification, are permitted provided that the following conditions
12999f82afSJohn Marino * are met:
13999f82afSJohn Marino * 1. Redistributions of source code must retain the above copyright
14999f82afSJohn Marino * notice, this list of conditions and the following disclaimer.
15999f82afSJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
16999f82afSJohn Marino * notice, this list of conditions and the following disclaimer in the
17999f82afSJohn Marino * documentation and/or other materials provided with the distribution.
18999f82afSJohn Marino *
19999f82afSJohn Marino * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20999f82afSJohn Marino * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21999f82afSJohn Marino * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22999f82afSJohn Marino * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23999f82afSJohn Marino * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24999f82afSJohn Marino * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25999f82afSJohn Marino * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26999f82afSJohn Marino * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27999f82afSJohn Marino * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28999f82afSJohn Marino * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29999f82afSJohn Marino * POSSIBILITY OF SUCH DAMAGE.
30999f82afSJohn Marino */
31999f82afSJohn Marino #include <sys/cdefs.h>
32999f82afSJohn Marino #include <sys/param.h>
33999f82afSJohn Marino #include <assert.h>
34999f82afSJohn Marino #include <stdio.h>
35999f82afSJohn Marino #include <string.h>
36999f82afSJohn Marino #include <stdlib.h>
37999f82afSJohn Marino #include <stdarg.h>
38999f82afSJohn Marino #include <stdint.h>
39999f82afSJohn Marino #include <stddef.h>
40999f82afSJohn Marino #include <unistd.h>
41999f82afSJohn Marino #include <fcntl.h>
42999f82afSJohn Marino #include <dlfcn.h>
43999f82afSJohn Marino #include <elf.h>
44999f82afSJohn Marino
45999f82afSJohn Marino #include "execinfo.h"
46999f82afSJohn Marino #include "symtab.h"
47999f82afSJohn Marino
48999f82afSJohn Marino #ifdef __linux__
49999f82afSJohn Marino #define SELF "/proc/self/exe"
50999f82afSJohn Marino #else
51999f82afSJohn Marino #include <sys/sysctl.h>
52999f82afSJohn Marino #define SELF "/proc/curproc/file"
53999f82afSJohn Marino #endif
54999f82afSJohn Marino
55999f82afSJohn Marino static int
open_self(int flags)56999f82afSJohn Marino open_self(int flags)
57999f82afSJohn Marino {
58999f82afSJohn Marino const char *pathname = SELF;
59999f82afSJohn Marino #ifdef KERN_PROC_PATHNAME
60999f82afSJohn Marino static const int name[] = {
61153c1b8fSzrj CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
62999f82afSJohn Marino };
63999f82afSJohn Marino char path[MAXPATHLEN];
64999f82afSJohn Marino size_t len;
65999f82afSJohn Marino
66999f82afSJohn Marino len = sizeof(path);
67999f82afSJohn Marino if (sysctl(name, 4, path, &len, NULL, 0) != -1)
68999f82afSJohn Marino pathname = path;
69999f82afSJohn Marino #endif
70999f82afSJohn Marino return open(pathname, flags);
71999f82afSJohn Marino }
72999f82afSJohn Marino
73999f82afSJohn Marino
74999f82afSJohn Marino static int __printflike(4, 5)
rasprintf(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,...)75999f82afSJohn Marino rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...)
76999f82afSJohn Marino {
77999f82afSJohn Marino for (;;) {
78999f82afSJohn Marino size_t nbufsiz;
79999f82afSJohn Marino char *nbuf;
80999f82afSJohn Marino
81999f82afSJohn Marino if (*buf && offs < *bufsiz) {
82999f82afSJohn Marino va_list ap;
83999f82afSJohn Marino int len;
84999f82afSJohn Marino
85999f82afSJohn Marino va_start(ap, fmt);
86999f82afSJohn Marino len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap);
87999f82afSJohn Marino va_end(ap);
88999f82afSJohn Marino
89999f82afSJohn Marino if (len < 0 || (size_t)len + 1 < *bufsiz - offs)
90999f82afSJohn Marino return len;
91999f82afSJohn Marino nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1);
92999f82afSJohn Marino } else
93999f82afSJohn Marino nbufsiz = MAX(offs, *bufsiz) + 512;
94999f82afSJohn Marino
95999f82afSJohn Marino nbuf = realloc(*buf, nbufsiz);
96999f82afSJohn Marino if (nbuf == NULL)
97999f82afSJohn Marino return -1;
98999f82afSJohn Marino *buf = nbuf;
99999f82afSJohn Marino *bufsiz = nbufsiz;
100999f82afSJohn Marino }
101999f82afSJohn Marino }
102999f82afSJohn Marino
103999f82afSJohn Marino /*
104999f82afSJohn Marino * format specifiers:
105999f82afSJohn Marino * %a = address
106999f82afSJohn Marino * %n = symbol_name
107999f82afSJohn Marino * %d = symbol_address - address
108999f82afSJohn Marino * %D = if symbol_address == address "" else +%d
109999f82afSJohn Marino * %f = filename
110999f82afSJohn Marino */
111999f82afSJohn Marino static ssize_t
format_string(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,Dl_info * dli,const void * addr)112999f82afSJohn Marino format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt,
113999f82afSJohn Marino Dl_info *dli, const void *addr)
114999f82afSJohn Marino {
115999f82afSJohn Marino ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr;
116999f82afSJohn Marino size_t o = offs;
117999f82afSJohn Marino int len;
118999f82afSJohn Marino
119999f82afSJohn Marino for (; *fmt; fmt++) {
120999f82afSJohn Marino if (*fmt != '%')
121999f82afSJohn Marino goto printone;
122999f82afSJohn Marino switch (*++fmt) {
123999f82afSJohn Marino case 'a':
124999f82afSJohn Marino len = rasprintf(buf, bufsiz, o, "%p", addr);
125999f82afSJohn Marino break;
126999f82afSJohn Marino case 'n':
127999f82afSJohn Marino len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname);
128999f82afSJohn Marino break;
129999f82afSJohn Marino case 'D':
130999f82afSJohn Marino if (diff)
131999f82afSJohn Marino len = rasprintf(buf, bufsiz, o, "+0x%tx", diff);
132999f82afSJohn Marino else
133999f82afSJohn Marino len = 0;
134999f82afSJohn Marino break;
135999f82afSJohn Marino case 'd':
136999f82afSJohn Marino len = rasprintf(buf, bufsiz, o, "0x%tx", diff);
137999f82afSJohn Marino break;
138999f82afSJohn Marino case 'f':
139999f82afSJohn Marino len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname);
140999f82afSJohn Marino break;
141999f82afSJohn Marino default:
142999f82afSJohn Marino printone:
143999f82afSJohn Marino len = rasprintf(buf, bufsiz, o, "%c", *fmt);
144999f82afSJohn Marino break;
145999f82afSJohn Marino }
146999f82afSJohn Marino if (len == -1)
147999f82afSJohn Marino return -1;
148999f82afSJohn Marino o += len;
149999f82afSJohn Marino }
150999f82afSJohn Marino return o - offs;
151999f82afSJohn Marino }
152999f82afSJohn Marino
153999f82afSJohn Marino static ssize_t
format_address(symtab_t * st,char ** buf,size_t * bufsiz,size_t offs,const char * fmt,const void * addr)154999f82afSJohn Marino format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs,
155999f82afSJohn Marino const char *fmt, const void *addr)
156999f82afSJohn Marino {
157999f82afSJohn Marino Dl_info dli;
158999f82afSJohn Marino
159999f82afSJohn Marino memset(&dli, 0, sizeof(dli));
160999f82afSJohn Marino (void)dladdr(addr, &dli);
161999f82afSJohn Marino if (st)
162999f82afSJohn Marino symtab_find(st, addr, &dli);
163999f82afSJohn Marino
164999f82afSJohn Marino if (dli.dli_sname == NULL)
165999f82afSJohn Marino dli.dli_sname = "???";
166999f82afSJohn Marino if (dli.dli_fname == NULL)
167999f82afSJohn Marino dli.dli_fname = "???";
168999f82afSJohn Marino if (dli.dli_saddr == NULL)
169999f82afSJohn Marino dli.dli_saddr = (void *)(intptr_t)addr;
170999f82afSJohn Marino
171999f82afSJohn Marino return format_string(buf, bufsiz, offs, fmt, &dli, addr);
172999f82afSJohn Marino }
173999f82afSJohn Marino
174999f82afSJohn Marino char **
backtrace_symbols_fmt(void * const * trace,size_t len,const char * fmt)175999f82afSJohn Marino backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt)
176999f82afSJohn Marino {
177999f82afSJohn Marino
178999f82afSJohn Marino static const size_t slen = sizeof(char *) + 64; /* estimate */
179*44c7130aSSascha Wildner size_t psize, offs;
180999f82afSJohn Marino char *ptr;
181999f82afSJohn Marino symtab_t *st;
182999f82afSJohn Marino int fd;
183999f82afSJohn Marino
184999f82afSJohn Marino if ((fd = open_self(O_RDONLY)) != -1)
185999f82afSJohn Marino st = symtab_create(fd, -1, STT_FUNC);
186999f82afSJohn Marino else
187999f82afSJohn Marino st = NULL;
188999f82afSJohn Marino
189999f82afSJohn Marino if ((ptr = calloc(len, slen)) == NULL)
190999f82afSJohn Marino goto out;
191999f82afSJohn Marino
192*44c7130aSSascha Wildner psize = len * slen;
193*44c7130aSSascha Wildner offs = len * sizeof(char *);
194999f82afSJohn Marino
195999f82afSJohn Marino /* We store only offsets in the first pass because of realloc */
196999f82afSJohn Marino for (size_t i = 0; i < len; i++) {
197999f82afSJohn Marino ssize_t x;
198999f82afSJohn Marino ((char **)(void *)ptr)[i] = (void *)offs;
199999f82afSJohn Marino x = format_address(st, &ptr, &psize, offs, fmt, trace[i]);
200999f82afSJohn Marino if (x == -1) {
201999f82afSJohn Marino free(ptr);
202999f82afSJohn Marino ptr = NULL;
203999f82afSJohn Marino goto out;
204999f82afSJohn Marino }
205999f82afSJohn Marino offs += x;
206999f82afSJohn Marino ptr[offs++] = '\0';
207999f82afSJohn Marino assert(offs < psize);
208999f82afSJohn Marino }
209999f82afSJohn Marino
210999f82afSJohn Marino /* Change offsets to pointers */
211999f82afSJohn Marino for (size_t j = 0; j < len; j++)
212999f82afSJohn Marino ((char **)(void *)ptr)[j] += (intptr_t)ptr;
213999f82afSJohn Marino
214999f82afSJohn Marino out:
215999f82afSJohn Marino symtab_destroy(st);
216999f82afSJohn Marino if (fd != -1)
217999f82afSJohn Marino (void)close(fd);
218999f82afSJohn Marino
219999f82afSJohn Marino return (void *)ptr;
220999f82afSJohn Marino }
221999f82afSJohn Marino
222999f82afSJohn Marino int
backtrace_symbols_fd_fmt(void * const * trace,size_t len,int fd,const char * fmt)223999f82afSJohn Marino backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd,
224999f82afSJohn Marino const char *fmt)
225999f82afSJohn Marino {
226999f82afSJohn Marino char **s = backtrace_symbols_fmt(trace, len, fmt);
227999f82afSJohn Marino if (s == NULL)
228999f82afSJohn Marino return -1;
229999f82afSJohn Marino for (size_t i = 0; i < len; i++)
230999f82afSJohn Marino if (dprintf(fd, "%s\n", s[i]) < 0)
231999f82afSJohn Marino break;
232999f82afSJohn Marino free(s);
233999f82afSJohn Marino return 0;
234999f82afSJohn Marino }
235999f82afSJohn Marino
236999f82afSJohn Marino static const char fmt[] = "%a <%n%D> at %f";
237999f82afSJohn Marino
238999f82afSJohn Marino char **
backtrace_symbols(void * const * trace,size_t len)239999f82afSJohn Marino backtrace_symbols(void *const *trace, size_t len)
240999f82afSJohn Marino {
241999f82afSJohn Marino return backtrace_symbols_fmt(trace, len, fmt);
242999f82afSJohn Marino }
243999f82afSJohn Marino
244999f82afSJohn Marino int
backtrace_symbols_fd(void * const * trace,size_t len,int fd)245999f82afSJohn Marino backtrace_symbols_fd(void *const *trace, size_t len, int fd)
246999f82afSJohn Marino {
247999f82afSJohn Marino return backtrace_symbols_fd_fmt(trace, len, fd, fmt);
248999f82afSJohn Marino }
249