xref: /freebsd/contrib/libexecinfo/backtrace.c (revision 27067774dce3388702a4cf744d7096c6fb71b688)
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