xref: /openbsd-src/gnu/lib/libexecinfo/backtrace.c (revision 771fbea0148a7fe458cf2c657edb98b32659f150)
1*771fbea0Smortimer /*	$OpenBSD: backtrace.c,v 1.1 2021/06/09 19:37:43 mortimer Exp $	*/
2*771fbea0Smortimer 
3*771fbea0Smortimer /*-
4*771fbea0Smortimer  * Copyright (c) 2012 The NetBSD Foundation, Inc.
5*771fbea0Smortimer  * All rights reserved.
6*771fbea0Smortimer  *
7*771fbea0Smortimer  * This code is derived from software contributed to The NetBSD Foundation
8*771fbea0Smortimer  * by Christos Zoulas.
9*771fbea0Smortimer  *
10*771fbea0Smortimer  * Redistribution and use in source and binary forms, with or without
11*771fbea0Smortimer  * modification, are permitted provided that the following conditions
12*771fbea0Smortimer  * are met:
13*771fbea0Smortimer  * 1. Redistributions of source code must retain the above copyright
14*771fbea0Smortimer  *    notice, this list of conditions and the following disclaimer.
15*771fbea0Smortimer  * 2. Redistributions in binary form must reproduce the above copyright
16*771fbea0Smortimer  *    notice, this list of conditions and the following disclaimer in the
17*771fbea0Smortimer  *    documentation and/or other materials provided with the distribution.
18*771fbea0Smortimer  *
19*771fbea0Smortimer  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20*771fbea0Smortimer  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21*771fbea0Smortimer  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22*771fbea0Smortimer  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23*771fbea0Smortimer  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24*771fbea0Smortimer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25*771fbea0Smortimer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26*771fbea0Smortimer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27*771fbea0Smortimer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28*771fbea0Smortimer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29*771fbea0Smortimer  * POSSIBILITY OF SUCH DAMAGE.
30*771fbea0Smortimer  */
31*771fbea0Smortimer #include <sys/cdefs.h>
32*771fbea0Smortimer 
33*771fbea0Smortimer #include <sys/param.h>
34*771fbea0Smortimer #include <assert.h>
35*771fbea0Smortimer #include <stdio.h>
36*771fbea0Smortimer #include <string.h>
37*771fbea0Smortimer #include <stdlib.h>
38*771fbea0Smortimer #include <stdarg.h>
39*771fbea0Smortimer #include <stdint.h>
40*771fbea0Smortimer #include <stddef.h>
41*771fbea0Smortimer #include <unistd.h>
42*771fbea0Smortimer #include <fcntl.h>
43*771fbea0Smortimer #include <dlfcn.h>
44*771fbea0Smortimer 
45*771fbea0Smortimer #include "execinfo.h"
46*771fbea0Smortimer 
47*771fbea0Smortimer static int
rasprintf(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,...)48*771fbea0Smortimer rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...)
49*771fbea0Smortimer {
50*771fbea0Smortimer 	for (;;) {
51*771fbea0Smortimer 		size_t nbufsiz;
52*771fbea0Smortimer 		char *nbuf;
53*771fbea0Smortimer 
54*771fbea0Smortimer 		if (*buf && offs < *bufsiz) {
55*771fbea0Smortimer 			va_list ap;
56*771fbea0Smortimer 			int len;
57*771fbea0Smortimer 
58*771fbea0Smortimer 			va_start(ap, fmt);
59*771fbea0Smortimer 			len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap);
60*771fbea0Smortimer 			va_end(ap);
61*771fbea0Smortimer 
62*771fbea0Smortimer 			if (len < 0 || (size_t)len + 1 < *bufsiz - offs)
63*771fbea0Smortimer 				return len;
64*771fbea0Smortimer 			nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1);
65*771fbea0Smortimer 		} else
66*771fbea0Smortimer 			nbufsiz = MAX(offs, *bufsiz) + 512;
67*771fbea0Smortimer 
68*771fbea0Smortimer 		nbuf = realloc(*buf, nbufsiz);
69*771fbea0Smortimer 		if (nbuf == NULL)
70*771fbea0Smortimer 			return -1;
71*771fbea0Smortimer 		*buf = nbuf;
72*771fbea0Smortimer 		*bufsiz = nbufsiz;
73*771fbea0Smortimer 	}
74*771fbea0Smortimer }
75*771fbea0Smortimer 
76*771fbea0Smortimer /*
77*771fbea0Smortimer  * format specifiers:
78*771fbea0Smortimer  *	%a	= address
79*771fbea0Smortimer  *	%n	= symbol_name
80*771fbea0Smortimer  *	%d	= symbol_address - address
81*771fbea0Smortimer  *	%D	= if symbol_address == address "" else +%d
82*771fbea0Smortimer  *	%f	= filename
83*771fbea0Smortimer  */
84*771fbea0Smortimer static ssize_t
format_string(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,Dl_info * dli,const void * addr)85*771fbea0Smortimer format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt,
86*771fbea0Smortimer     Dl_info *dli, const void *addr)
87*771fbea0Smortimer {
88*771fbea0Smortimer 	ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr;
89*771fbea0Smortimer 	size_t o = offs;
90*771fbea0Smortimer 	int len;
91*771fbea0Smortimer 
92*771fbea0Smortimer 	for (; *fmt; fmt++) {
93*771fbea0Smortimer 		if (*fmt != '%')
94*771fbea0Smortimer 			goto printone;
95*771fbea0Smortimer 		switch (*++fmt) {
96*771fbea0Smortimer 		case 'a':
97*771fbea0Smortimer 			len = rasprintf(buf, bufsiz, o, "%p", addr);
98*771fbea0Smortimer 			break;
99*771fbea0Smortimer 		case 'n':
100*771fbea0Smortimer 			len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname);
101*771fbea0Smortimer 			break;
102*771fbea0Smortimer 		case 'D':
103*771fbea0Smortimer 			if (diff)
104*771fbea0Smortimer 				len = rasprintf(buf, bufsiz, o, "+0x%tx", diff);
105*771fbea0Smortimer 			else
106*771fbea0Smortimer 				len = 0;
107*771fbea0Smortimer 			break;
108*771fbea0Smortimer 		case 'd':
109*771fbea0Smortimer 			len = rasprintf(buf, bufsiz, o, "0x%tx", diff);
110*771fbea0Smortimer 			break;
111*771fbea0Smortimer 		case 'f':
112*771fbea0Smortimer 			len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname);
113*771fbea0Smortimer 			break;
114*771fbea0Smortimer 		default:
115*771fbea0Smortimer 		printone:
116*771fbea0Smortimer 			len = rasprintf(buf, bufsiz, o, "%c", *fmt);
117*771fbea0Smortimer 			break;
118*771fbea0Smortimer 		}
119*771fbea0Smortimer 		if (len == -1)
120*771fbea0Smortimer 			return -1;
121*771fbea0Smortimer 		o += len;
122*771fbea0Smortimer 	}
123*771fbea0Smortimer 	return o - offs;
124*771fbea0Smortimer }
125*771fbea0Smortimer 
126*771fbea0Smortimer static ssize_t
format_address(char ** buf,size_t * bufsiz,size_t offs,const char * fmt,const void * addr)127*771fbea0Smortimer format_address(char **buf, size_t *bufsiz, size_t offs,
128*771fbea0Smortimer     const char *fmt, const void *addr)
129*771fbea0Smortimer {
130*771fbea0Smortimer 	Dl_info dli;
131*771fbea0Smortimer 
132*771fbea0Smortimer 	memset(&dli, 0, sizeof(dli));
133*771fbea0Smortimer 	(void)dladdr(addr, &dli);
134*771fbea0Smortimer 
135*771fbea0Smortimer 	if (dli.dli_sname == NULL)
136*771fbea0Smortimer 		dli.dli_sname = "???";
137*771fbea0Smortimer 	if (dli.dli_fname == NULL)
138*771fbea0Smortimer 		dli.dli_fname = "???";
139*771fbea0Smortimer 	if (dli.dli_saddr == NULL)
140*771fbea0Smortimer 		dli.dli_saddr = (void *)(intptr_t)addr;
141*771fbea0Smortimer 
142*771fbea0Smortimer 	return format_string(buf, bufsiz, offs, fmt, &dli, addr);
143*771fbea0Smortimer }
144*771fbea0Smortimer 
145*771fbea0Smortimer char **
backtrace_symbols_fmt(void * const * trace,size_t len,const char * fmt)146*771fbea0Smortimer backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt)
147*771fbea0Smortimer {
148*771fbea0Smortimer 
149*771fbea0Smortimer 	static const size_t slen = sizeof(char *) + 64;	/* estimate */
150*771fbea0Smortimer 	char *ptr;
151*771fbea0Smortimer 
152*771fbea0Smortimer 	if ((ptr = calloc(len, slen)) == NULL)
153*771fbea0Smortimer 		goto out;
154*771fbea0Smortimer 
155*771fbea0Smortimer 	size_t psize = len * slen;
156*771fbea0Smortimer 	size_t offs = len * sizeof(char *);
157*771fbea0Smortimer 
158*771fbea0Smortimer 	/* We store only offsets in the first pass because of realloc */
159*771fbea0Smortimer 	for (size_t i = 0; i < len; i++) {
160*771fbea0Smortimer 		ssize_t x;
161*771fbea0Smortimer 		((char **)(void *)ptr)[i] = (void *)offs;
162*771fbea0Smortimer 		x = format_address(&ptr, &psize, offs, fmt, trace[i]);
163*771fbea0Smortimer 		if (x == -1) {
164*771fbea0Smortimer 			free(ptr);
165*771fbea0Smortimer 			ptr = NULL;
166*771fbea0Smortimer 			goto out;
167*771fbea0Smortimer 		}
168*771fbea0Smortimer 		offs += x;
169*771fbea0Smortimer 		ptr[offs++] = '\0';
170*771fbea0Smortimer 		assert(offs < psize);
171*771fbea0Smortimer 	}
172*771fbea0Smortimer 
173*771fbea0Smortimer 	/* Change offsets to pointers */
174*771fbea0Smortimer 	for (size_t j = 0; j < len; j++)
175*771fbea0Smortimer 		((char **)(void *)ptr)[j] += (intptr_t)ptr;
176*771fbea0Smortimer 
177*771fbea0Smortimer out:
178*771fbea0Smortimer 	return (void *)ptr;
179*771fbea0Smortimer }
180*771fbea0Smortimer 
181*771fbea0Smortimer int
backtrace_symbols_fd_fmt(void * const * trace,size_t len,int fd,const char * fmt)182*771fbea0Smortimer backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd,
183*771fbea0Smortimer     const char *fmt)
184*771fbea0Smortimer {
185*771fbea0Smortimer 	char **s = backtrace_symbols_fmt(trace, len, fmt);
186*771fbea0Smortimer 	if (s == NULL)
187*771fbea0Smortimer 		return -1;
188*771fbea0Smortimer 	for (size_t i = 0; i < len; i++)
189*771fbea0Smortimer 		if (dprintf(fd, "%s\n", s[i]) < 0)
190*771fbea0Smortimer 			break;
191*771fbea0Smortimer 	free(s);
192*771fbea0Smortimer 	return 0;
193*771fbea0Smortimer }
194*771fbea0Smortimer 
195*771fbea0Smortimer static const char fmt[] = "%a <%n%D> at %f";
196*771fbea0Smortimer 
197*771fbea0Smortimer char **
backtrace_symbols(void * const * trace,size_t len)198*771fbea0Smortimer backtrace_symbols(void *const *trace, size_t len)
199*771fbea0Smortimer {
200*771fbea0Smortimer 	return backtrace_symbols_fmt(trace, len, fmt);
201*771fbea0Smortimer }
202*771fbea0Smortimer 
203*771fbea0Smortimer int
backtrace_symbols_fd(void * const * trace,size_t len,int fd)204*771fbea0Smortimer backtrace_symbols_fd(void *const *trace, size_t len, int fd)
205*771fbea0Smortimer {
206*771fbea0Smortimer 	return backtrace_symbols_fd_fmt(trace, len, fd, fmt);
207*771fbea0Smortimer }
208