1*1c512be1Schristos /* $NetBSD: backtrace.c,v 1.9 2025/01/23 12:08:12 christos Exp $ */ 283d64dcfSchristos 383d64dcfSchristos /*- 483d64dcfSchristos * Copyright (c) 2012 The NetBSD Foundation, Inc. 583d64dcfSchristos * All rights reserved. 683d64dcfSchristos * 783d64dcfSchristos * This code is derived from software contributed to The NetBSD Foundation 883d64dcfSchristos * by Christos Zoulas. 983d64dcfSchristos * 1083d64dcfSchristos * Redistribution and use in source and binary forms, with or without 1183d64dcfSchristos * modification, are permitted provided that the following conditions 1283d64dcfSchristos * are met: 1383d64dcfSchristos * 1. Redistributions of source code must retain the above copyright 1483d64dcfSchristos * notice, this list of conditions and the following disclaimer. 1583d64dcfSchristos * 2. Redistributions in binary form must reproduce the above copyright 1683d64dcfSchristos * notice, this list of conditions and the following disclaimer in the 1783d64dcfSchristos * documentation and/or other materials provided with the distribution. 1883d64dcfSchristos * 1983d64dcfSchristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2083d64dcfSchristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2183d64dcfSchristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2283d64dcfSchristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2383d64dcfSchristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2483d64dcfSchristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2583d64dcfSchristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2683d64dcfSchristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2783d64dcfSchristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2883d64dcfSchristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2983d64dcfSchristos * POSSIBILITY OF SUCH DAMAGE. 3083d64dcfSchristos */ 3183d64dcfSchristos #include <sys/cdefs.h> 32*1c512be1Schristos __RCSID("$NetBSD: backtrace.c,v 1.9 2025/01/23 12:08:12 christos Exp $"); 3383d64dcfSchristos 3483d64dcfSchristos #include <sys/param.h> 3583d64dcfSchristos #include <assert.h> 3683d64dcfSchristos #include <stdio.h> 3783d64dcfSchristos #include <string.h> 3883d64dcfSchristos #include <stdlib.h> 3983d64dcfSchristos #include <stdarg.h> 4083d64dcfSchristos #include <stdint.h> 4183d64dcfSchristos #include <stddef.h> 4283d64dcfSchristos #include <unistd.h> 4383d64dcfSchristos #include <fcntl.h> 4483d64dcfSchristos #include <dlfcn.h> 4583d64dcfSchristos #include <elf.h> 4683d64dcfSchristos 4783d64dcfSchristos #include "execinfo.h" 4844889fb9Sskrll #include "symbol.h" 4983d64dcfSchristos #include "symtab.h" 5083d64dcfSchristos 5183d64dcfSchristos #ifdef __linux__ 5283d64dcfSchristos #define SELF "/proc/self/exe" 5383d64dcfSchristos #else 54e6ada316Schristos #include <sys/sysctl.h> 5583d64dcfSchristos #define SELF "/proc/curproc/file" 5683d64dcfSchristos #endif 5783d64dcfSchristos 58*1c512be1Schristos static int self_fd = -1; 59*1c512be1Schristos 60e6ada316Schristos static int 61e6ada316Schristos open_self(int flags) 62e6ada316Schristos { 63e6ada316Schristos const char *pathname = SELF; 64e6ada316Schristos #ifdef KERN_PROC_PATHNAME 65e6ada316Schristos static const int name[] = { 66e09cabeeSchristos CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME, 67e6ada316Schristos }; 68e6ada316Schristos char path[MAXPATHLEN]; 69e6ada316Schristos size_t len; 70e6ada316Schristos 71e6ada316Schristos len = sizeof(path); 72e6ada316Schristos if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1) 73e6ada316Schristos pathname = path; 74e6ada316Schristos #endif 75e6ada316Schristos return open(pathname, flags); 76e6ada316Schristos } 77e6ada316Schristos 78*1c512be1Schristos int 79*1c512be1Schristos backtrace_sandbox_init(void) 80*1c512be1Schristos { 81*1c512be1Schristos 82*1c512be1Schristos if (self_fd == -1) 83*1c512be1Schristos self_fd = open_self(O_RDONLY); 84*1c512be1Schristos return self_fd >= 0 ? 0 : -1; 85*1c512be1Schristos } 86*1c512be1Schristos 87*1c512be1Schristos void 88*1c512be1Schristos backtrace_sandbox_fini(void) 89*1c512be1Schristos { 90*1c512be1Schristos assert(self_fd >= 0); 91*1c512be1Schristos 92*1c512be1Schristos close(self_fd); 93*1c512be1Schristos self_fd = -1; 94*1c512be1Schristos } 95e6ada316Schristos 9683d64dcfSchristos static int __printflike(4, 5) 9783d64dcfSchristos rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...) 9883d64dcfSchristos { 9983d64dcfSchristos for (;;) { 10083d64dcfSchristos size_t nbufsiz; 10183d64dcfSchristos char *nbuf; 10283d64dcfSchristos 10383d64dcfSchristos if (*buf && offs < *bufsiz) { 10483d64dcfSchristos va_list ap; 10583d64dcfSchristos int len; 10683d64dcfSchristos 10783d64dcfSchristos va_start(ap, fmt); 10883d64dcfSchristos len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap); 10983d64dcfSchristos va_end(ap); 11083d64dcfSchristos 1119e14bd8cSchristos if (len < 0 || (size_t)len + 1 < *bufsiz - offs) 11283d64dcfSchristos return len; 11383d64dcfSchristos nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1); 11483d64dcfSchristos } else 11583d64dcfSchristos nbufsiz = MAX(offs, *bufsiz) + 512; 11683d64dcfSchristos 11783d64dcfSchristos nbuf = realloc(*buf, nbufsiz); 11883d64dcfSchristos if (nbuf == NULL) 11983d64dcfSchristos return -1; 12083d64dcfSchristos *buf = nbuf; 12183d64dcfSchristos *bufsiz = nbufsiz; 12283d64dcfSchristos } 12383d64dcfSchristos } 12483d64dcfSchristos 12583d64dcfSchristos /* 12683d64dcfSchristos * format specifiers: 12783d64dcfSchristos * %a = address 12883d64dcfSchristos * %n = symbol_name 12983d64dcfSchristos * %d = symbol_address - address 13083d64dcfSchristos * %D = if symbol_address == address "" else +%d 13183d64dcfSchristos * %f = filename 13283d64dcfSchristos */ 13383d64dcfSchristos static ssize_t 13483d64dcfSchristos format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt, 13583d64dcfSchristos Dl_info *dli, const void *addr) 13683d64dcfSchristos { 13744889fb9Sskrll const uintptr_t symaddr = SYMBOL_CANONICALIZE(dli->dli_saddr); 13844889fb9Sskrll ptrdiff_t diff = (const char *)addr - (const char *)symaddr; 13983d64dcfSchristos size_t o = offs; 14083d64dcfSchristos int len; 14183d64dcfSchristos 14283d64dcfSchristos for (; *fmt; fmt++) { 14383d64dcfSchristos if (*fmt != '%') 14483d64dcfSchristos goto printone; 14583d64dcfSchristos switch (*++fmt) { 14683d64dcfSchristos case 'a': 14783d64dcfSchristos len = rasprintf(buf, bufsiz, o, "%p", addr); 14883d64dcfSchristos break; 14983d64dcfSchristos case 'n': 15083d64dcfSchristos len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname); 15183d64dcfSchristos break; 15283d64dcfSchristos case 'D': 15383d64dcfSchristos if (diff) 15483d64dcfSchristos len = rasprintf(buf, bufsiz, o, "+0x%tx", diff); 15583d64dcfSchristos else 15683d64dcfSchristos len = 0; 15783d64dcfSchristos break; 15883d64dcfSchristos case 'd': 15983d64dcfSchristos len = rasprintf(buf, bufsiz, o, "0x%tx", diff); 16083d64dcfSchristos break; 16183d64dcfSchristos case 'f': 16283d64dcfSchristos len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname); 16383d64dcfSchristos break; 16483d64dcfSchristos default: 16583d64dcfSchristos printone: 16683d64dcfSchristos len = rasprintf(buf, bufsiz, o, "%c", *fmt); 16783d64dcfSchristos break; 16883d64dcfSchristos } 16983d64dcfSchristos if (len == -1) 17083d64dcfSchristos return -1; 17183d64dcfSchristos o += len; 17283d64dcfSchristos } 17383d64dcfSchristos return o - offs; 17483d64dcfSchristos } 17583d64dcfSchristos 17683d64dcfSchristos static ssize_t 17783d64dcfSchristos format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs, 17883d64dcfSchristos const char *fmt, const void *addr) 17983d64dcfSchristos { 18083d64dcfSchristos Dl_info dli; 18183d64dcfSchristos 18283d64dcfSchristos memset(&dli, 0, sizeof(dli)); 18383d64dcfSchristos (void)dladdr(addr, &dli); 18483d64dcfSchristos if (st) 18583d64dcfSchristos symtab_find(st, addr, &dli); 18683d64dcfSchristos 18783d64dcfSchristos if (dli.dli_sname == NULL) 18883d64dcfSchristos dli.dli_sname = "???"; 18983d64dcfSchristos if (dli.dli_fname == NULL) 19083d64dcfSchristos dli.dli_fname = "???"; 19183d64dcfSchristos if (dli.dli_saddr == NULL) 19283d64dcfSchristos dli.dli_saddr = (void *)(intptr_t)addr; 19383d64dcfSchristos 19483d64dcfSchristos return format_string(buf, bufsiz, offs, fmt, &dli, addr); 19583d64dcfSchristos } 19683d64dcfSchristos 19783d64dcfSchristos char ** 19883d64dcfSchristos backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt) 19983d64dcfSchristos { 20083d64dcfSchristos 20183d64dcfSchristos static const size_t slen = sizeof(char *) + 64; /* estimate */ 20283d64dcfSchristos char *ptr; 20383d64dcfSchristos symtab_t *st; 204*1c512be1Schristos int fd = self_fd; 20583d64dcfSchristos 206*1c512be1Schristos if (fd != -1 || (fd = open_self(O_RDONLY)) != -1) 20783d64dcfSchristos st = symtab_create(fd, -1, STT_FUNC); 20883d64dcfSchristos else 20983d64dcfSchristos st = NULL; 21083d64dcfSchristos 21183d64dcfSchristos if ((ptr = calloc(len, slen)) == NULL) 212ac9e9457Schristos goto out; 21383d64dcfSchristos 21483d64dcfSchristos size_t psize = len * slen; 21583d64dcfSchristos size_t offs = len * sizeof(char *); 21683d64dcfSchristos 21783d64dcfSchristos /* We store only offsets in the first pass because of realloc */ 21883d64dcfSchristos for (size_t i = 0; i < len; i++) { 21983d64dcfSchristos ssize_t x; 22083d64dcfSchristos ((char **)(void *)ptr)[i] = (void *)offs; 22183d64dcfSchristos x = format_address(st, &ptr, &psize, offs, fmt, trace[i]); 22283d64dcfSchristos if (x == -1) { 22383d64dcfSchristos free(ptr); 224ac9e9457Schristos ptr = NULL; 225ac9e9457Schristos goto out; 22683d64dcfSchristos } 22783d64dcfSchristos offs += x; 22883d64dcfSchristos ptr[offs++] = '\0'; 22983d64dcfSchristos assert(offs < psize); 23083d64dcfSchristos } 23183d64dcfSchristos 23283d64dcfSchristos /* Change offsets to pointers */ 23383d64dcfSchristos for (size_t j = 0; j < len; j++) 23483d64dcfSchristos ((char **)(void *)ptr)[j] += (intptr_t)ptr; 23583d64dcfSchristos 236ac9e9457Schristos out: 23783d64dcfSchristos symtab_destroy(st); 238*1c512be1Schristos if (fd != -1 && fd != self_fd) 23983d64dcfSchristos (void)close(fd); 24083d64dcfSchristos 24183d64dcfSchristos return (void *)ptr; 24283d64dcfSchristos } 24383d64dcfSchristos 24483d64dcfSchristos int 24583d64dcfSchristos backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd, 24683d64dcfSchristos const char *fmt) 24783d64dcfSchristos { 24883d64dcfSchristos char **s = backtrace_symbols_fmt(trace, len, fmt); 24983d64dcfSchristos if (s == NULL) 25083d64dcfSchristos return -1; 25183d64dcfSchristos for (size_t i = 0; i < len; i++) 25283d64dcfSchristos if (dprintf(fd, "%s\n", s[i]) < 0) 25383d64dcfSchristos break; 25483d64dcfSchristos free(s); 25583d64dcfSchristos return 0; 25683d64dcfSchristos } 25783d64dcfSchristos 25883d64dcfSchristos static const char fmt[] = "%a <%n%D> at %f"; 25983d64dcfSchristos 26083d64dcfSchristos char ** 26183d64dcfSchristos backtrace_symbols(void *const *trace, size_t len) 26283d64dcfSchristos { 26383d64dcfSchristos return backtrace_symbols_fmt(trace, len, fmt); 26483d64dcfSchristos } 26583d64dcfSchristos 26683d64dcfSchristos int 26783d64dcfSchristos backtrace_symbols_fd(void *const *trace, size_t len, int fd) 26883d64dcfSchristos { 26983d64dcfSchristos return backtrace_symbols_fd_fmt(trace, len, fd, fmt); 27083d64dcfSchristos } 271