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