diff --git a/lib/libexecinfo/Makefile b/lib/libexecinfo/Makefile new file mode 100644 index 000000000000..4768c6a86fac --- /dev/null +++ b/lib/libexecinfo/Makefile @@ -0,0 +1,31 @@ +# $NetBSD: Makefile,v 1.1 2012/05/26 22:02:29 christos Exp $ + +.include + +USE_UNWIND=yes +WARNS?=4 +INCSDIR=/usr/include +INCS=execinfo.h + +#CPPFLAGS+='-D__RCSID(a)=' -D_GNU_SOURCE '-D__printflike(a,b)=' +#CPPFLAGS+=-I/usr/include/libelf +#COPTS+=-std=gnu99 + +LIBDPLIBS+= elf ${NETBSDSRCDIR}/external/bsd/libelf/lib + +LIB=execinfo +SRCS=symtab.c backtrace.c +MAN= backtrace.3 + +.if ${USE_UNWIND} == "yes" +SRCS+=unwind.c +.else +SRCS+=builtin.c +.endif + +MLINKS+= backtrace.3 backtrace_symbols.3 +MLINKS+= backtrace.3 backtrace_symbols_fmt.3 +MLINKS+= backtrace.3 backtrace_symbols_fd.3 +MLINKS+= backtrace.3 backtrace_symbols_fd_fmt.3 + +.include diff --git a/lib/libexecinfo/backtrace.3 b/lib/libexecinfo/backtrace.3 new file mode 100644 index 000000000000..0c618aa0a975 --- /dev/null +++ b/lib/libexecinfo/backtrace.3 @@ -0,0 +1,161 @@ +.\" $NetBSD: backtrace.3,v 1.1 2012/05/26 22:02:29 christos Exp $ +.\" +.\" Copyright (c) 2012 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Christos Zoulas +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd January 24, 2012 +.Dt BACKTRACE 3 +.Os +.Sh NAME +.Nm backtrace +.Nd fill in the backtrace of the currently executing thread. +.Sh LIBRARY +.Lb libexecinfo +.Sh SYNOPSIS +.In execinfo.h +.Ft size_t +.Fn backtrace "void **addrlist" "size_t len" +.Ft "char **" +.Fn backtrace_symbols "void * const *addrlist" "size_t len" +.Ft int +.Fn backtrace_symbols_fd "void * const *addrlist" "size_t len" "int fd" +.Ft "char **" +.Fn backtrace_symbols_fmt "void * const *addrlist" "size_t len" "const char *fmt" +.Ft int +.Fn backtrace_symbols_fmt_fd "void * const *addrlist" "size_t len" "const char *fmt" "int fd" +.Sh DESCRIPTION +The +.Fn backtrace +function places into the array pointed by +.Fa addrlist +the array of the values of the program counter for each frame called up to +.Fa len +frames. +The number of frames found (which can be fewer than +.Fa len ) +is returned. +.Pp +The +.Fn backtrace_symbols_fmt +function, takes an array of previously filled addresses from +.Fn backtrace +in +.Fa addrlist +of +.Fa len +elements, and uses +.Fa fmt +to format them. +The formatting characters available are: +.Bl -tag -width a -offset indent +.It Dv a +The numeric address of each element as would be printed using %p. +.It Dv n +The name of the nearest function symbol (smaller than the address element) +as determined by +.Xr dladdr 3 +if the symbol was dynamic, or looked up in the executable if static and +the /proc filesystem is available to determine the executable path. +.It Dv d +The difference of the symbol address and the address element printed +using 0x%tx. +.It Dv D +The difference of the symbol addresss and the address element printed using ++0x%tx if non-zero, or nothing if zero. +.It Dv f +The filename of the symbol as determined by +.Xr dladdr 3 . +.El +.Pp +The array of formatted strings is returned as a contiguous memory address which +can be freed by a single +.Xr free 3 . +.Pp +The +.Fn backtrace_symbols +function is equivalent of calling +.Fn backtrace_symbols_fmt +with a format argument of +.Dv "%a <%n%D> at %f" +.Pp +The +.Fn backtrace_symbols_fd +and +.Fn backtrace_symbols_fmt_fd +are similar to the non _fd named functions, only instead of returning +an array or strings, they print a new-line separated array of strings in +fd, and return +.Dv 0 +on success and +.Dv \-1 +on failure. +.Sh RETURN VALUES +The +.Fn backtrace +function returns the number of elements tht were filled in the backtrace. +The +.Fn backtrace_symbols +and +.Fn backtrace_symbols_fmt +return a string array on success, and +.Dv NULL +on failure, setting +.Va errno . +.\" XXX +Diagnostic output may also be produced, by the ELF symbol lookup functions. +function returns a pointer to a string that is the parent directory of +.Sh BUGS +.Bl -enum +.It +Errors should not be printed but communicated to the caller differently. +.It +Because these functions use +.Xr elf 3 +this is a separate library instead of be part of libc/libutil so that library +dependencies are not introduced. +.It +The linux versions of the functions (there are no _fmt variants), use +.Ft int +instead of +.Ft size_t +arguments. +.It +Since +.Xr dladdr 3 +only deals with dynamic symbols, we need to find the symbols from the main +portion of the program. +For that we need to locate the executable, and we use procfs for to find it +which is not portable. +.El +.Sh SEE ALSO +.Xr elf 3 +.Xr dladdr 3 +.Sh HISTORY +The +.Fn backtrace +library of functions first appeared in +.Nx 6 . diff --git a/lib/libexecinfo/backtrace.c b/lib/libexecinfo/backtrace.c new file mode 100644 index 000000000000..2e592ce5ce3f --- /dev/null +++ b/lib/libexecinfo/backtrace.c @@ -0,0 +1,227 @@ +/* $NetBSD: backtrace.c,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: backtrace.c,v 1.1 2012/05/26 22:02:29 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "execinfo.h" +#include "symtab.h" + +#ifdef __linux__ +#define SELF "/proc/self/exe" +#else +#define SELF "/proc/curproc/file" +#endif + +static int __printflike(4, 5) +rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...) +{ + for (;;) { + size_t nbufsiz; + char *nbuf; + + if (*buf && offs < *bufsiz) { + va_list ap; + int len; + + va_start(ap, fmt); + len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap); + va_end(ap); + + if (len < 0 || (size_t)len < *bufsiz - offs) + return len; + nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1); + } else + nbufsiz = MAX(offs, *bufsiz) + 512; + + nbuf = realloc(*buf, nbufsiz); + if (nbuf == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + } +} + +/* + * format specifiers: + * %a = address + * %n = symbol_name + * %d = symbol_address - address + * %D = if symbol_address == address "" else +%d + * %f = filename + */ +static ssize_t +format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt, + Dl_info *dli, const void *addr) +{ + ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr; + size_t o = offs; + int len; + + for (; *fmt; fmt++) { + if (*fmt != '%') + goto printone; + switch (*++fmt) { + case 'a': + len = rasprintf(buf, bufsiz, o, "%p", addr); + break; + case 'n': + len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname); + break; + case 'D': + if (diff) + len = rasprintf(buf, bufsiz, o, "+0x%tx", diff); + else + len = 0; + break; + case 'd': + len = rasprintf(buf, bufsiz, o, "0x%tx", diff); + break; + case 'f': + len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname); + break; + default: + printone: + len = rasprintf(buf, bufsiz, o, "%c", *fmt); + break; + } + if (len == -1) + return -1; + o += len; + } + return o - offs; +} + +static ssize_t +format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs, + const char *fmt, const void *addr) +{ + Dl_info dli; + + memset(&dli, 0, sizeof(dli)); + (void)dladdr(addr, &dli); + if (st) + symtab_find(st, addr, &dli); + + if (dli.dli_sname == NULL) + dli.dli_sname = "???"; + if (dli.dli_fname == NULL) + dli.dli_fname = "???"; + if (dli.dli_saddr == NULL) + dli.dli_saddr = (void *)(intptr_t)addr; + + return format_string(buf, bufsiz, offs, fmt, &dli, addr); +} + +char ** +backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt) +{ + + static const size_t slen = sizeof(char *) + 64; /* estimate */ + char *ptr; + symtab_t *st; + int fd; + + if ((fd = open(SELF, O_RDONLY)) != -1) + st = symtab_create(fd, -1, STT_FUNC); + else + st = NULL; + + if ((ptr = calloc(len, slen)) == NULL) + return NULL; + + size_t psize = len * slen; + size_t offs = len * sizeof(char *); + + /* We store only offsets in the first pass because of realloc */ + for (size_t i = 0; i < len; i++) { + ssize_t x; + ((char **)(void *)ptr)[i] = (void *)offs; + x = format_address(st, &ptr, &psize, offs, fmt, trace[i]); + if (x == -1) { + free(ptr); + return NULL; + } + offs += x; + ptr[offs++] = '\0'; + assert(offs < psize); + } + + /* Change offsets to pointers */ + for (size_t j = 0; j < len; j++) + ((char **)(void *)ptr)[j] += (intptr_t)ptr; + + symtab_destroy(st); + if (fd != -1) + (void)close(fd); + + return (void *)ptr; +} + +int +backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd, + const char *fmt) +{ + char **s = backtrace_symbols_fmt(trace, len, fmt); + if (s == NULL) + return -1; + for (size_t i = 0; i < len; i++) + if (dprintf(fd, "%s\n", s[i]) < 0) + break; + free(s); + return 0; +} + +static const char fmt[] = "%a <%n%D> at %f"; + +char ** +backtrace_symbols(void *const *trace, size_t len) +{ + return backtrace_symbols_fmt(trace, len, fmt); +} + +int +backtrace_symbols_fd(void *const *trace, size_t len, int fd) +{ + return backtrace_symbols_fd_fmt(trace, len, fd, fmt); +} diff --git a/lib/libexecinfo/builtin.c b/lib/libexecinfo/builtin.c new file mode 100644 index 000000000000..3fc861958d15 --- /dev/null +++ b/lib/libexecinfo/builtin.c @@ -0,0 +1,68 @@ +/* $NetBSD: builtin.c,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: builtin.c,v 1.1 2012/05/26 22:02:29 christos Exp $"); + +#include +#include + +#include "execinfo.h" + +#ifdef __MACHINE_STACK_GROWS_UP +#define BELOW > +#else +#define BELOW < +#endif + +#ifdef __lint__ +#define __builtin_frame_address(a) ((void *)a) +#endif + +struct frameinfo { + struct frameinfo *next; + void *return_address; +}; + +size_t +backtrace(void **trace, size_t len) +{ + const struct frameinfo *frame = __builtin_frame_address(0); + void *stack = &stack; + + for (size_t i = 0; i < len; i++) { + if ((const void *)frame BELOW stack) + return i; + trace[i] = frame->return_address; + frame = frame->next; + } + + return len; +} diff --git a/lib/libexecinfo/execinfo.h b/lib/libexecinfo/execinfo.h new file mode 100644 index 000000000000..7968b0da6bea --- /dev/null +++ b/lib/libexecinfo/execinfo.h @@ -0,0 +1,42 @@ +/* $NetBSD: execinfo.h,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _EXECINFO_H_ +#define _EXECINFO_H_ + +__BEGIN_DECLS +size_t backtrace(void **, size_t); +char **backtrace_symbols(void *const *, size_t); +int backtrace_symbols_fd(void *const *, size_t, int); +char **backtrace_symbols_fmt(void *const *, size_t, const char *); +int backtrace_symbols_fd_fmt(void *const *, size_t, int, const char *); +__END_DECLS + +#endif /* _EXECINFO_H_ */ diff --git a/lib/libexecinfo/shlib_version b/lib/libexecinfo/shlib_version new file mode 100644 index 000000000000..3995abd3db1b --- /dev/null +++ b/lib/libexecinfo/shlib_version @@ -0,0 +1,3 @@ +# $NetBSD: shlib_version,v 1.1 2012/05/26 22:02:29 christos Exp $ +major=0 +minor=0 diff --git a/lib/libexecinfo/symtab.c b/lib/libexecinfo/symtab.c new file mode 100644 index 000000000000..b5da75cefad7 --- /dev/null +++ b/lib/libexecinfo/symtab.c @@ -0,0 +1,192 @@ +/* $NetBSD: symtab.c,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: symtab.c,v 1.1 2012/05/26 22:02:29 christos Exp $"); + +#include +#include +#include +#include +#include + +#include +#include +#ifndef ELF_ST_BIND +#define ELF_ST_BIND(x) ((x) >> 4) +#endif +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) +#endif + + +#include "symtab.h" + +struct symbol { + char *st_name; + uintptr_t st_value; + uintptr_t st_info; +}; + +struct symtab { + size_t nsymbols; + struct symbol *symbols; +}; + +static int +address_compare(const void *a, const void *b) +{ + const struct symbol *sa = a; + const struct symbol *sb = b; + return (int)(intmax_t)(sa->st_value - sb->st_value); +} + +void +symtab_destroy(symtab_t *s) +{ + if (s == NULL) + return; + for (size_t i = 0; i < s->nsymbols; i++) + free(s->symbols[i].st_name); + free(s->symbols); + free(s); +} + +symtab_t * +symtab_create(int fd, int bind, int type) +{ + Elf *elf; + symtab_t *st; + Elf_Scn *scn = NULL; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warnx("Elf Library is out of date."); + return NULL; + } + + elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + warnx("Error opening elf file: %s", elf_errmsg(elf_errno())); + return NULL; + } + st = calloc(1, sizeof(*st)); + if (st == NULL) { + warnx("Error allocating symbol table"); + elf_end(elf); + return NULL; + } + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + GElf_Shdr shdr; + Elf_Data *edata; + size_t ns; + struct symbol *s; + + gelf_getshdr(scn, &shdr); + if(shdr.sh_type != SHT_SYMTAB) + continue; + + edata = elf_getdata(scn, NULL); + ns = shdr.sh_size / shdr.sh_entsize; + s = calloc(ns, sizeof(*s)); + if (s == NULL) { + warn("Cannot allocate %zu symbols", ns); + goto out; + } + st->symbols = s; + + for (size_t i = 0; i < ns; i++) { + GElf_Sym sym; + gelf_getsym(edata, (int)i, &sym); + + if (bind != -1 && + (unsigned)bind != ELF_ST_BIND(sym.st_info)) + continue; + + if (type != -1 && + (unsigned)type != ELF_ST_TYPE(sym.st_info)) + continue; + + s->st_value = sym.st_value; + s->st_info = sym.st_info; + s->st_name = strdup( + elf_strptr(elf, shdr.sh_link, sym.st_name)); + if (s->st_name == NULL) + goto out; + s++; + } + st->nsymbols = s - st->symbols; + if (st->nsymbols == 0) { + warnx("No symbols found"); + goto out; + } + qsort(st->symbols, st->nsymbols, sizeof(*st->symbols), + address_compare); + elf_end(elf); + return st; + } +out: + symtab_destroy(st); + elf_end(elf); + return NULL; +} + + +int +symtab_find(const symtab_t *st, const void *p, Dl_info *dli) +{ + struct symbol *s = st->symbols; + size_t ns = st->nsymbols; + size_t hi = ns; + size_t lo = 0; + size_t mid = ns / 2; + uintptr_t dd, sd, me = (uintptr_t)p; + + for (;;) { + if (s[mid].st_value < me) + lo = mid; + else if (s[mid].st_value > me) + hi = mid; + else + break; + if (hi - lo == 1) { + mid = lo; + break; + } + mid = (hi + lo) / 2; + } + dd = me - (uintptr_t)dli->dli_saddr; + sd = me - s[mid].st_value; + if (dd > sd) { + dli->dli_saddr = (void *)s[mid].st_value; + dli->dli_sname = s[mid].st_name; + } + return 1; +} diff --git a/lib/libexecinfo/symtab.h b/lib/libexecinfo/symtab.h new file mode 100644 index 000000000000..d527a3cc4729 --- /dev/null +++ b/lib/libexecinfo/symtab.h @@ -0,0 +1,42 @@ +/* $NetBSD: symtab.h,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _SYMTAB_H_ +#define _SYMTAB_H_ + +__BEGIN_DECLS +typedef struct symtab symtab_t; + +void symtab_destroy(symtab_t *); +symtab_t * symtab_create(int, int, int); +int symtab_find(const symtab_t *, const void *, Dl_info *); +__END_DECLS + +#endif /* _SYMTAB_H_ */ diff --git a/lib/libexecinfo/unwind.c b/lib/libexecinfo/unwind.c new file mode 100644 index 000000000000..b9b785ff6021 --- /dev/null +++ b/lib/libexecinfo/unwind.c @@ -0,0 +1,72 @@ +/* $NetBSD: unwind.c,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include + +#include "unwind.h" +#include "execinfo.h" + +struct tracer_context { + void **arr; + size_t len; + size_t n; +}; + +static _Unwind_Reason_Code +tracer(struct _Unwind_Context *ctx, void *arg) +{ + struct tracer_context *t = arg; + if (t->n == (size_t)~0) { + /* Skip backtrace frame */ + t->n = 0; + return 0; + } + if (t->n < t->len) + t->arr[t->n++] = _Unwind_GetIP(ctx); + return 0; +} + +size_t +backtrace(void **arr, size_t len) +{ + struct tracer_context ctx; + + ctx.arr = arr; + ctx.len = len; + ctx.n = (size_t)~0; + + _Unwind_Backtrace(tracer, &ctx); + if (ctx.n != (size_t)~0 && ctx.n > 0) + ctx.arr[--ctx.n] = NULL; /* Skip frame below __start */ + + return ctx.n; +} diff --git a/lib/libexecinfo/unwind.h b/lib/libexecinfo/unwind.h new file mode 100644 index 000000000000..c9978b3d5df5 --- /dev/null +++ b/lib/libexecinfo/unwind.h @@ -0,0 +1,68 @@ +/* $NetBSD: unwind.h,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _UNWIND_H_ +#define _UNWIND_H_ + +__BEGIN_DECLS +struct _Unwind_Context; +struct _Unwind_Exception; +typedef int _Unwind_Reason_Code; +typedef void *_Unwind_Ptr; +typedef long _Unwind_Word; + +typedef _Unwind_Reason_Code + (*_Unwind_Trace_Fn)(struct _Unwind_Context *, void *); +#ifdef notyet +typedef _Unwind_Reason_Code + (*_Unwind_Stop_Fn)(struct _Unwind_Context *, void *); +#endif + +_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); +void _Unwind_DeleteException(struct _Unwind_Exception *); +void *_Unwind_FindEnclosingFunction(void *); +#ifdef notyet +_Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *, + _Unwind_Stop_fn, void *); +#endif +_Unwind_Word _Unwind_GetCFA(struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetDataRelBase(struct _Unwind_Context *); +_Unwind_Word _Unwind_GetGR(struct _Unwind_Context *, int); +_Unwind_Ptr _Unwind_GetIP(struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetIPInfo(struct _Unwind_Context *, int *); +void *_Unwind_GetLanguageSpecificData( + struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetRegionStart(struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetTextRelBase(struct _Unwind_Context *); +_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *); +void _Unwind_Resume(struct _Unwind_Exception *); +_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *); +void _Unwind_SetGR(struct _Unwind_Context *, int, + _Unwind_Ptr); +void _Unwind_SetIP(struct _Unwind_Context *, _Unwind_Ptr); +__END_DECLS +#endif /* _UNWIND_H_ */