1f357c00bSEd Maste /* $NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $ */
2287472b3SEd Maste
3287472b3SEd Maste /*-
4287472b3SEd Maste * Copyright (c) 2012 The NetBSD Foundation, Inc.
5287472b3SEd Maste * All rights reserved.
6287472b3SEd Maste *
7287472b3SEd Maste * This code is derived from software contributed to The NetBSD Foundation
8287472b3SEd Maste * by Christos Zoulas.
9287472b3SEd Maste *
10287472b3SEd Maste * Redistribution and use in source and binary forms, with or without
11287472b3SEd Maste * modification, are permitted provided that the following conditions
12287472b3SEd Maste * are met:
13287472b3SEd Maste * 1. Redistributions of source code must retain the above copyright
14287472b3SEd Maste * notice, this list of conditions and the following disclaimer.
15287472b3SEd Maste * 2. Redistributions in binary form must reproduce the above copyright
16287472b3SEd Maste * notice, this list of conditions and the following disclaimer in the
17287472b3SEd Maste * documentation and/or other materials provided with the distribution.
18287472b3SEd Maste *
19287472b3SEd Maste * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20287472b3SEd Maste * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21287472b3SEd Maste * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22287472b3SEd Maste * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23287472b3SEd Maste * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24287472b3SEd Maste * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25287472b3SEd Maste * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26287472b3SEd Maste * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27287472b3SEd Maste * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28287472b3SEd Maste * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29287472b3SEd Maste * POSSIBILITY OF SUCH DAMAGE.
30287472b3SEd Maste */
31287472b3SEd Maste #include <sys/cdefs.h>
32f357c00bSEd Maste __RCSID("$NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $");
33287472b3SEd Maste
34287472b3SEd Maste #include <sys/param.h>
35287472b3SEd Maste #include <assert.h>
36287472b3SEd Maste #include <stdio.h>
37287472b3SEd Maste #include <string.h>
38287472b3SEd Maste #include <stdlib.h>
39287472b3SEd Maste #include <stdarg.h>
40287472b3SEd Maste #include <stdint.h>
41287472b3SEd Maste #include <stddef.h>
42287472b3SEd Maste #include <unistd.h>
43287472b3SEd Maste #include <fcntl.h>
44287472b3SEd Maste #include <dlfcn.h>
45287472b3SEd Maste #include <elf.h>
46287472b3SEd Maste
47287472b3SEd Maste #include "execinfo.h"
48287472b3SEd Maste #include "symtab.h"
49287472b3SEd Maste
50287472b3SEd Maste #ifdef __linux__
51287472b3SEd Maste #define SELF "/proc/self/exe"
52287472b3SEd Maste #else
53f357c00bSEd Maste #include <sys/sysctl.h>
54287472b3SEd Maste #define SELF "/proc/curproc/file"
55287472b3SEd Maste #endif
56287472b3SEd Maste
57f357c00bSEd Maste static int
open_self(int flags)58f357c00bSEd Maste open_self(int flags)
59f357c00bSEd Maste {
60f357c00bSEd Maste const char *pathname = SELF;
61f357c00bSEd Maste #ifdef KERN_PROC_PATHNAME
62f357c00bSEd Maste static const int name[] = {
63f357c00bSEd Maste CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
64f357c00bSEd Maste };
65f357c00bSEd Maste char path[MAXPATHLEN];
66f357c00bSEd Maste size_t len;
67f357c00bSEd Maste
68f357c00bSEd Maste len = sizeof(path);
69f357c00bSEd Maste if (sysctl(name, 4, path, &len, NULL, 0) != -1)
70f357c00bSEd Maste pathname = path;
71f357c00bSEd Maste #endif
72f357c00bSEd Maste return open(pathname, flags);
73f357c00bSEd Maste }
74f357c00bSEd Maste
75f357c00bSEd Maste
76287472b3SEd Maste static int __printflike(4, 5)
rasprintf(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,...)77287472b3SEd Maste rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...)
78287472b3SEd Maste {
79287472b3SEd Maste for (;;) {
80287472b3SEd Maste size_t nbufsiz;
81287472b3SEd Maste char *nbuf;
82287472b3SEd Maste
83287472b3SEd Maste if (*buf && offs < *bufsiz) {
84287472b3SEd Maste va_list ap;
85287472b3SEd Maste int len;
86287472b3SEd Maste
87287472b3SEd Maste va_start(ap, fmt);
88287472b3SEd Maste len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap);
89287472b3SEd Maste va_end(ap);
90287472b3SEd Maste
91*3e7aca6fSEd Maste if (len < 0 || (size_t)len + 1 < *bufsiz - offs)
92287472b3SEd Maste return len;
93287472b3SEd Maste nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1);
94287472b3SEd Maste } else
95287472b3SEd Maste nbufsiz = MAX(offs, *bufsiz) + 512;
96287472b3SEd Maste
97287472b3SEd Maste nbuf = realloc(*buf, nbufsiz);
98287472b3SEd Maste if (nbuf == NULL)
99287472b3SEd Maste return -1;
100287472b3SEd Maste *buf = nbuf;
101287472b3SEd Maste *bufsiz = nbufsiz;
102287472b3SEd Maste }
103287472b3SEd Maste }
104287472b3SEd Maste
105287472b3SEd Maste /*
106287472b3SEd Maste * format specifiers:
107287472b3SEd Maste * %a = address
108287472b3SEd Maste * %n = symbol_name
109287472b3SEd Maste * %d = symbol_address - address
110287472b3SEd Maste * %D = if symbol_address == address "" else +%d
111287472b3SEd Maste * %f = filename
112287472b3SEd Maste */
113287472b3SEd Maste static ssize_t
format_string(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,Dl_info * dli,const void * addr)114287472b3SEd Maste format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt,
115287472b3SEd Maste Dl_info *dli, const void *addr)
116287472b3SEd Maste {
117287472b3SEd Maste ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr;
118287472b3SEd Maste size_t o = offs;
119287472b3SEd Maste int len;
120287472b3SEd Maste
121287472b3SEd Maste for (; *fmt; fmt++) {
122287472b3SEd Maste if (*fmt != '%')
123287472b3SEd Maste goto printone;
124287472b3SEd Maste switch (*++fmt) {
125287472b3SEd Maste case 'a':
126287472b3SEd Maste len = rasprintf(buf, bufsiz, o, "%p", addr);
127287472b3SEd Maste break;
128287472b3SEd Maste case 'n':
129287472b3SEd Maste len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname);
130287472b3SEd Maste break;
131287472b3SEd Maste case 'D':
132287472b3SEd Maste if (diff)
133287472b3SEd Maste len = rasprintf(buf, bufsiz, o, "+0x%tx", diff);
134287472b3SEd Maste else
135287472b3SEd Maste len = 0;
136287472b3SEd Maste break;
137287472b3SEd Maste case 'd':
138287472b3SEd Maste len = rasprintf(buf, bufsiz, o, "0x%tx", diff);
139287472b3SEd Maste break;
140287472b3SEd Maste case 'f':
141287472b3SEd Maste len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname);
142287472b3SEd Maste break;
143287472b3SEd Maste default:
144287472b3SEd Maste printone:
145287472b3SEd Maste len = rasprintf(buf, bufsiz, o, "%c", *fmt);
146287472b3SEd Maste break;
147287472b3SEd Maste }
148287472b3SEd Maste if (len == -1)
149287472b3SEd Maste return -1;
150287472b3SEd Maste o += len;
151287472b3SEd Maste }
152287472b3SEd Maste return o - offs;
153287472b3SEd Maste }
154287472b3SEd Maste
155287472b3SEd Maste static ssize_t
format_address(symtab_t * st,char ** buf,size_t * bufsiz,size_t offs,const char * fmt,const void * addr)156287472b3SEd Maste format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs,
157287472b3SEd Maste const char *fmt, const void *addr)
158287472b3SEd Maste {
159287472b3SEd Maste Dl_info dli;
160287472b3SEd Maste
161287472b3SEd Maste memset(&dli, 0, sizeof(dli));
162287472b3SEd Maste (void)dladdr(addr, &dli);
163287472b3SEd Maste if (st)
164287472b3SEd Maste symtab_find(st, addr, &dli);
165287472b3SEd Maste
166287472b3SEd Maste if (dli.dli_sname == NULL)
167287472b3SEd Maste dli.dli_sname = "???";
168287472b3SEd Maste if (dli.dli_fname == NULL)
169287472b3SEd Maste dli.dli_fname = "???";
170287472b3SEd Maste if (dli.dli_saddr == NULL)
171287472b3SEd Maste dli.dli_saddr = (void *)(intptr_t)addr;
172287472b3SEd Maste
173287472b3SEd Maste return format_string(buf, bufsiz, offs, fmt, &dli, addr);
174287472b3SEd Maste }
175287472b3SEd Maste
176287472b3SEd Maste char **
backtrace_symbols_fmt(void * const * trace,size_t len,const char * fmt)177287472b3SEd Maste backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt)
178287472b3SEd Maste {
179287472b3SEd Maste
180287472b3SEd Maste static const size_t slen = sizeof(char *) + 64; /* estimate */
181287472b3SEd Maste char *ptr;
182287472b3SEd Maste symtab_t *st;
183287472b3SEd Maste int fd;
184287472b3SEd Maste
185f357c00bSEd Maste if ((fd = open_self(O_RDONLY)) != -1)
186287472b3SEd Maste st = symtab_create(fd, -1, STT_FUNC);
187287472b3SEd Maste else
188287472b3SEd Maste st = NULL;
189287472b3SEd Maste
190287472b3SEd Maste if ((ptr = calloc(len, slen)) == NULL)
191287472b3SEd Maste goto out;
192287472b3SEd Maste
193287472b3SEd Maste size_t psize = len * slen;
194287472b3SEd Maste size_t offs = len * sizeof(char *);
195287472b3SEd Maste
196287472b3SEd Maste /* We store only offsets in the first pass because of realloc */
197287472b3SEd Maste for (size_t i = 0; i < len; i++) {
198287472b3SEd Maste ssize_t x;
199287472b3SEd Maste ((char **)(void *)ptr)[i] = (void *)offs;
200287472b3SEd Maste x = format_address(st, &ptr, &psize, offs, fmt, trace[i]);
201287472b3SEd Maste if (x == -1) {
202287472b3SEd Maste free(ptr);
203287472b3SEd Maste ptr = NULL;
204287472b3SEd Maste goto out;
205287472b3SEd Maste }
206287472b3SEd Maste offs += x;
207287472b3SEd Maste ptr[offs++] = '\0';
208287472b3SEd Maste assert(offs < psize);
209287472b3SEd Maste }
210287472b3SEd Maste
211287472b3SEd Maste /* Change offsets to pointers */
212287472b3SEd Maste for (size_t j = 0; j < len; j++)
213287472b3SEd Maste ((char **)(void *)ptr)[j] += (intptr_t)ptr;
214287472b3SEd Maste
215287472b3SEd Maste out:
216287472b3SEd Maste symtab_destroy(st);
217287472b3SEd Maste if (fd != -1)
218287472b3SEd Maste (void)close(fd);
219287472b3SEd Maste
220287472b3SEd Maste return (void *)ptr;
221287472b3SEd Maste }
222287472b3SEd Maste
223287472b3SEd Maste int
backtrace_symbols_fd_fmt(void * const * trace,size_t len,int fd,const char * fmt)224287472b3SEd Maste backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd,
225287472b3SEd Maste const char *fmt)
226287472b3SEd Maste {
227287472b3SEd Maste char **s = backtrace_symbols_fmt(trace, len, fmt);
228287472b3SEd Maste if (s == NULL)
229287472b3SEd Maste return -1;
230287472b3SEd Maste for (size_t i = 0; i < len; i++)
231287472b3SEd Maste if (dprintf(fd, "%s\n", s[i]) < 0)
232287472b3SEd Maste break;
233287472b3SEd Maste free(s);
234287472b3SEd Maste return 0;
235287472b3SEd Maste }
236287472b3SEd Maste
237287472b3SEd Maste static const char fmt[] = "%a <%n%D> at %f";
238287472b3SEd Maste
239287472b3SEd Maste char **
backtrace_symbols(void * const * trace,size_t len)240287472b3SEd Maste backtrace_symbols(void *const *trace, size_t len)
241287472b3SEd Maste {
242287472b3SEd Maste return backtrace_symbols_fmt(trace, len, fmt);
243287472b3SEd Maste }
244287472b3SEd Maste
245287472b3SEd Maste int
backtrace_symbols_fd(void * const * trace,size_t len,int fd)246287472b3SEd Maste backtrace_symbols_fd(void *const *trace, size_t len, int fd)
247287472b3SEd Maste {
248287472b3SEd Maste return backtrace_symbols_fd_fmt(trace, len, fd, fmt);
249287472b3SEd Maste }
250