xref: /dflybsd-src/lib/libexecinfo/backtrace.c (revision 44c7130a3f9c507ef56c3d9dc855105d1b481e5c)
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