1255033Semaste/*	$NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $	*/
2254661Semaste
3254661Semaste/*-
4254661Semaste * Copyright (c) 2012 The NetBSD Foundation, Inc.
5254661Semaste * All rights reserved.
6254661Semaste *
7254661Semaste * This code is derived from software contributed to The NetBSD Foundation
8254661Semaste * by Christos Zoulas.
9254661Semaste *
10254661Semaste * Redistribution and use in source and binary forms, with or without
11254661Semaste * modification, are permitted provided that the following conditions
12254661Semaste * are met:
13254661Semaste * 1. Redistributions of source code must retain the above copyright
14254661Semaste *    notice, this list of conditions and the following disclaimer.
15254661Semaste * 2. Redistributions in binary form must reproduce the above copyright
16254661Semaste *    notice, this list of conditions and the following disclaimer in the
17254661Semaste *    documentation and/or other materials provided with the distribution.
18254661Semaste *
19254661Semaste * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20254661Semaste * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21254661Semaste * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22254661Semaste * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23254661Semaste * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24254661Semaste * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25254661Semaste * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26254661Semaste * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27254661Semaste * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28254661Semaste * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29254661Semaste * POSSIBILITY OF SUCH DAMAGE.
30254661Semaste */
31254661Semaste#include <sys/cdefs.h>
32255033Semaste__RCSID("$NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $");
33254661Semaste
34254661Semaste#include <sys/param.h>
35254661Semaste#include <assert.h>
36254689Semaste#define _WITH_DPRINTF
37254661Semaste#include <stdio.h>
38254661Semaste#include <string.h>
39254661Semaste#include <stdlib.h>
40254661Semaste#include <stdarg.h>
41254661Semaste#include <stdint.h>
42254661Semaste#include <stddef.h>
43254661Semaste#include <unistd.h>
44254661Semaste#include <fcntl.h>
45254661Semaste#include <dlfcn.h>
46254661Semaste#include <elf.h>
47254661Semaste
48254661Semaste#include "execinfo.h"
49254661Semaste#include "symtab.h"
50254661Semaste
51254661Semaste#ifdef __linux__
52254661Semaste#define SELF	"/proc/self/exe"
53254661Semaste#else
54255033Semaste#include <sys/sysctl.h>
55254661Semaste#define SELF	"/proc/curproc/file"
56254661Semaste#endif
57254661Semaste
58255033Semastestatic int
59255033Semasteopen_self(int flags)
60255033Semaste{
61255033Semaste	const char *pathname = SELF;
62255033Semaste#ifdef KERN_PROC_PATHNAME
63255033Semaste	static const int name[] = {
64255033Semaste		CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
65255033Semaste	};
66255033Semaste	char path[MAXPATHLEN];
67255033Semaste	size_t len;
68255033Semaste
69255033Semaste	len = sizeof(path);
70255033Semaste	if (sysctl(name, 4, path, &len, NULL, 0) != -1)
71255033Semaste		pathname = path;
72255033Semaste#endif
73255033Semaste	return open(pathname, flags);
74255033Semaste}
75255033Semaste
76255033Semaste
77254661Semastestatic int __printflike(4, 5)
78254661Semasterasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...)
79254661Semaste{
80254661Semaste	for (;;) {
81254661Semaste		size_t nbufsiz;
82254661Semaste		char *nbuf;
83254661Semaste
84254661Semaste		if (*buf && offs < *bufsiz) {
85254661Semaste			va_list ap;
86254661Semaste			int len;
87254661Semaste
88254661Semaste			va_start(ap, fmt);
89254661Semaste			len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap);
90254661Semaste			va_end(ap);
91254661Semaste
92258558Semaste			if (len < 0 || (size_t)len + 1 < *bufsiz - offs)
93254661Semaste				return len;
94254661Semaste			nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1);
95254661Semaste		} else
96254661Semaste			nbufsiz = MAX(offs, *bufsiz) + 512;
97254661Semaste
98254661Semaste		nbuf = realloc(*buf, nbufsiz);
99254661Semaste		if (nbuf == NULL)
100254661Semaste			return -1;
101254661Semaste		*buf = nbuf;
102254661Semaste		*bufsiz = nbufsiz;
103254661Semaste	}
104254661Semaste}
105254661Semaste
106254661Semaste/*
107254661Semaste * format specifiers:
108254661Semaste *	%a	= address
109254661Semaste *	%n	= symbol_name
110254661Semaste *	%d	= symbol_address - address
111254661Semaste *	%D	= if symbol_address == address "" else +%d
112254661Semaste *	%f	= filename
113254661Semaste */
114254661Semastestatic ssize_t
115254661Semasteformat_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt,
116254661Semaste    Dl_info *dli, const void *addr)
117254661Semaste{
118254661Semaste	ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr;
119254661Semaste	size_t o = offs;
120254661Semaste	int len;
121254661Semaste
122254661Semaste	for (; *fmt; fmt++) {
123254661Semaste		if (*fmt != '%')
124254661Semaste			goto printone;
125254661Semaste		switch (*++fmt) {
126254661Semaste		case 'a':
127254661Semaste			len = rasprintf(buf, bufsiz, o, "%p", addr);
128254661Semaste			break;
129254661Semaste		case 'n':
130254661Semaste			len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname);
131254661Semaste			break;
132254661Semaste		case 'D':
133254661Semaste			if (diff)
134254661Semaste				len = rasprintf(buf, bufsiz, o, "+0x%tx", diff);
135254661Semaste			else
136254661Semaste				len = 0;
137254661Semaste			break;
138254661Semaste		case 'd':
139254661Semaste			len = rasprintf(buf, bufsiz, o, "0x%tx", diff);
140254661Semaste			break;
141254661Semaste		case 'f':
142254661Semaste			len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname);
143254661Semaste			break;
144254661Semaste		default:
145254661Semaste		printone:
146254661Semaste			len = rasprintf(buf, bufsiz, o, "%c", *fmt);
147254661Semaste			break;
148254661Semaste		}
149254661Semaste		if (len == -1)
150254661Semaste			return -1;
151254661Semaste		o += len;
152254661Semaste	}
153254661Semaste	return o - offs;
154254661Semaste}
155254661Semaste
156254661Semastestatic ssize_t
157254661Semasteformat_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs,
158254661Semaste    const char *fmt, const void *addr)
159254661Semaste{
160254661Semaste	Dl_info dli;
161254661Semaste
162254661Semaste	memset(&dli, 0, sizeof(dli));
163254661Semaste	(void)dladdr(addr, &dli);
164254661Semaste	if (st)
165254661Semaste		symtab_find(st, addr, &dli);
166254661Semaste
167254661Semaste	if (dli.dli_sname == NULL)
168254661Semaste		dli.dli_sname = "???";
169254661Semaste	if (dli.dli_fname == NULL)
170254661Semaste		dli.dli_fname = "???";
171254661Semaste	if (dli.dli_saddr == NULL)
172254661Semaste		dli.dli_saddr = (void *)(intptr_t)addr;
173254661Semaste
174254661Semaste	return format_string(buf, bufsiz, offs, fmt, &dli, addr);
175254661Semaste}
176254661Semaste
177254661Semastechar **
178254661Semastebacktrace_symbols_fmt(void *const *trace, size_t len, const char *fmt)
179254661Semaste{
180254661Semaste
181254661Semaste	static const size_t slen = sizeof(char *) + 64;	/* estimate */
182254661Semaste	char *ptr;
183254661Semaste	symtab_t *st;
184254661Semaste	int fd;
185254661Semaste
186255033Semaste	if ((fd = open_self(O_RDONLY)) != -1)
187254661Semaste		st = symtab_create(fd, -1, STT_FUNC);
188254661Semaste	else
189254661Semaste		st = NULL;
190254661Semaste
191254661Semaste	if ((ptr = calloc(len, slen)) == NULL)
192254661Semaste		goto out;
193254661Semaste
194254661Semaste	size_t psize = len * slen;
195254661Semaste	size_t offs = len * sizeof(char *);
196254661Semaste
197254661Semaste	/* We store only offsets in the first pass because of realloc */
198254661Semaste	for (size_t i = 0; i < len; i++) {
199254661Semaste		ssize_t x;
200254661Semaste		((char **)(void *)ptr)[i] = (void *)offs;
201254661Semaste		x = format_address(st, &ptr, &psize, offs, fmt, trace[i]);
202254661Semaste		if (x == -1) {
203254661Semaste			free(ptr);
204254661Semaste			ptr = NULL;
205254661Semaste			goto out;
206254661Semaste		}
207254661Semaste		offs += x;
208254661Semaste		ptr[offs++] = '\0';
209254661Semaste		assert(offs < psize);
210254661Semaste	}
211254661Semaste
212254661Semaste	/* Change offsets to pointers */
213254661Semaste	for (size_t j = 0; j < len; j++)
214254661Semaste		((char **)(void *)ptr)[j] += (intptr_t)ptr;
215254661Semaste
216254661Semasteout:
217254661Semaste	symtab_destroy(st);
218254661Semaste	if (fd != -1)
219254661Semaste		(void)close(fd);
220254661Semaste
221254661Semaste	return (void *)ptr;
222254661Semaste}
223254661Semaste
224254661Semasteint
225254661Semastebacktrace_symbols_fd_fmt(void *const *trace, size_t len, int fd,
226254661Semaste    const char *fmt)
227254661Semaste{
228254661Semaste	char **s = backtrace_symbols_fmt(trace, len, fmt);
229254661Semaste	if (s == NULL)
230254661Semaste		return -1;
231254661Semaste	for (size_t i = 0; i < len; i++)
232254661Semaste		if (dprintf(fd, "%s\n", s[i]) < 0)
233254661Semaste			break;
234254661Semaste	free(s);
235254661Semaste	return 0;
236254661Semaste}
237254661Semaste
238254661Semastestatic const char fmt[] = "%a <%n%D> at %f";
239254661Semaste
240254661Semastechar **
241254661Semastebacktrace_symbols(void *const *trace, size_t len)
242254661Semaste{
243254661Semaste	return backtrace_symbols_fmt(trace, len, fmt);
244254661Semaste}
245254661Semaste
246254661Semasteint
247254661Semastebacktrace_symbols_fd(void *const *trace, size_t len, int fd)
248254661Semaste{
249254661Semaste	return backtrace_symbols_fd_fmt(trace, len, fd, fmt);
250254661Semaste}
251