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