Add libexecinfo.
authormortimer <mortimer@openbsd.org>
Wed, 9 Jun 2021 19:37:43 +0000 (19:37 +0000)
committermortimer <mortimer@openbsd.org>
Wed, 9 Jun 2021 19:37:43 +0000 (19:37 +0000)
Based on NetBSD implementation, without the libelf dependency. Architectures
which have libunwind use libunwind, and others use a stub implementation
that does nothing since __builtin methods are unreliable.

Much feedback and help from jca@.

ok kettenis@ sthen@

gnu/lib/libexecinfo/Makefile [new file with mode: 0644]
gnu/lib/libexecinfo/Symbols.map [new file with mode: 0644]
gnu/lib/libexecinfo/backtrace.3 [new file with mode: 0644]
gnu/lib/libexecinfo/backtrace.c [new file with mode: 0644]
gnu/lib/libexecinfo/builtin.c [new file with mode: 0644]
gnu/lib/libexecinfo/execinfo.h [new file with mode: 0644]
gnu/lib/libexecinfo/shlib_version [new file with mode: 0644]
gnu/lib/libexecinfo/unwind.c [new file with mode: 0644]

diff --git a/gnu/lib/libexecinfo/Makefile b/gnu/lib/libexecinfo/Makefile
new file mode 100644 (file)
index 0000000..fe0855a
--- /dev/null
@@ -0,0 +1,55 @@
+# $OpenBSD: Makefile,v 1.1 2021/06/09 19:37:43 mortimer Exp $
+
+.include <bsd.own.mk>
+
+USE_UNWIND=no
+.if ${COMPILER_VERSION} == "clang"
+USE_UNWIND=yes
+.endif
+
+LIB=execinfo
+SRCS=backtrace.c
+MAN= backtrace.3
+
+.if empty(CFLAGS:M-std=*)
+CFLAGS+=  -std=gnu99
+.endif
+
+.if ${USE_UNWIND} == "yes"
+.PATH: ${SRCDIR} ${BSDSRCDIR}/gnu/llvm/libunwind/src
+.PATH: ${SRCDIR} ${BSDSRCDIR}/gnu/llvm/libcxx/src
+CXXFLAGS+= -I${BSDSRCDIR}/gnu/llvm/libunwind/include \
+           -I${BSDSRCDIR}/gnu/llvm/libcxx/include
+CFLAGS+= -I${BSDSRCDIR}/gnu/llvm/libunwind/include
+
+CPPFLAGS+=  -D_LIBUNWIND_IS_NATIVE_ONLY
+CPPFLAGS+=  -DLIBUNWIND_USE_WEAK_PTHREAD
+CPPFLAGS+=  -DNDEBUG
+CXXFLAGS+=  -nostdlib -nostdinc++ -funwind-tables \
+            -fno-exceptions -fno-rtti
+.if empty(CXXFLAGS:M-std=*)
+CXXFLAGS+=  -std=c++11
+.endif
+
+SRCS+=unwind.c \
+       Unwind-EHABI.cpp \
+       Unwind-sjlj.c \
+       UnwindLevel1-gcc-ext.c \
+       UnwindLevel1.c \
+       UnwindRegistersRestore.S \
+       UnwindRegistersSave.S \
+       libunwind.cpp \
+       new.cpp
+
+.else # !${USE_UNWIND}
+SRCS+=builtin.c
+.endif
+
+VERSION_SCRIPT=        ${.CURDIR}/Symbols.map
+
+includes:
+       cmp -s ${DESTDIR}/usr/include/execinfo.h ${.CURDIR}/execinfo.h || \
+               ${INSTALL} ${INSTALL_COPY} -m 444 -o $(BINOWN) -g $(BINGRP) \
+               ${.CURDIR}/execinfo.h ${DESTDIR}/usr/include/execinfo.h
+
+.include <bsd.lib.mk>
diff --git a/gnu/lib/libexecinfo/Symbols.map b/gnu/lib/libexecinfo/Symbols.map
new file mode 100644 (file)
index 0000000..c3ea4d6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       global:
+               backtrace;
+               backtrace_symbols;
+               backtrace_symbols_fd;
+               backtrace_symbols_fmt;
+               backtrace_symbols_fd_fmt;
+       local:
+               *;
+};
diff --git a/gnu/lib/libexecinfo/backtrace.3 b/gnu/lib/libexecinfo/backtrace.3
new file mode 100644 (file)
index 0000000..99ddf91
--- /dev/null
@@ -0,0 +1,152 @@
+.\"    $OpenBSD: backtrace.3,v 1.1 2021/06/09 19:37:43 mortimer Exp $
+.\"
+.\" Copyright (c) 2021 Todd Mortimer <mortimer@openbsd.org>
+.\" 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 November 5, 2015
+.Dt BACKTRACE 3
+.Os
+.Sh NAME
+.Nm backtrace ,
+.Nm backtrace_symbols ,
+.Nm backtrace_symbols_fd ,
+.Nm backtrace_symbols_fmt ,
+.Nm backtrace_symbols_fd_fmt
+.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_fd_fmt "void * const *addrlist" "size_t len" "int fd" "const char *fmt"
+.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
+.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 address 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
+.Dq "%a <%n%D> at %f"
+.Pp
+The
+.Fn backtrace_symbols_fd
+and
+.Fn backtrace_symbols_fd_fmt
+are similar to the non _fd named functions, only instead of returning
+an array of 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 that 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 .
+.Sh SEE ALSO
+.Xr dladdr 3 ,
+.Sh HISTORY
+The
+.Fn backtrace
+library of functions first appeared in
+.Nx 7.0
+and was imported into
+.Ox 7.0 .
+.Sh BUGS
+.Bl -enum
+.It
+Only unwinding with libunwind is supported.
+On architectures without libunwind the
+.Fn backtrace
+function simply returns 0.
+.It
+Since
+.Xr dladdr 3
+only deals with dynamic symbols, local symbols from the main
+portion of the program are not printed.
+.El
diff --git a/gnu/lib/libexecinfo/backtrace.c b/gnu/lib/libexecinfo/backtrace.c
new file mode 100644 (file)
index 0000000..c967b0e
--- /dev/null
@@ -0,0 +1,207 @@
+/*     $OpenBSD: backtrace.c,v 1.1 2021/06/09 19:37:43 mortimer 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 <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+
+#include "execinfo.h"
+
+static int
+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 + 1 < *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(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 (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;
+
+       if ((ptr = calloc(len, slen)) == NULL)
+               goto out;
+
+       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(&ptr, &psize, offs, fmt, trace[i]);
+               if (x == -1) {
+                       free(ptr);
+                       ptr = NULL;
+                       goto out;
+               }
+               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;
+
+out:
+       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/gnu/lib/libexecinfo/builtin.c b/gnu/lib/libexecinfo/builtin.c
new file mode 100644 (file)
index 0000000..7bc60f7
--- /dev/null
@@ -0,0 +1,25 @@
+/*     $OpenBSD: builtin.c,v 1.1 2021/06/09 19:37:43 mortimer Exp $    */
+
+/*
+ * Copyright (c) 2021 Todd Mortimer <mortimer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "execinfo.h"
+
+size_t
+backtrace(void **trace, size_t len)
+{
+  return 0;
+}
diff --git a/gnu/lib/libexecinfo/execinfo.h b/gnu/lib/libexecinfo/execinfo.h
new file mode 100644 (file)
index 0000000..d0017de
--- /dev/null
@@ -0,0 +1,45 @@
+/*     $OpenBSD: execinfo.h,v 1.1 2021/06/09 19:37:43 mortimer 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_
+
+#include <sys/cdefs.h>
+#include <stddef.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/gnu/lib/libexecinfo/shlib_version b/gnu/lib/libexecinfo/shlib_version
new file mode 100644 (file)
index 0000000..1f3ecfd
--- /dev/null
@@ -0,0 +1,3 @@
+#      $OpenBSD: shlib_version,v 1.1 2021/06/09 19:37:43 mortimer Exp $
+major=3
+minor=0
diff --git a/gnu/lib/libexecinfo/unwind.c b/gnu/lib/libexecinfo/unwind.c
new file mode 100644 (file)
index 0000000..e852710
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $OpenBSD: unwind.c,v 1.1 2021/06/09 19:37:43 mortimer 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 <sys/cdefs.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#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++] = (void *)_Unwind_GetIP(ctx);
+       else
+               return _URC_FOREIGN_EXCEPTION_CAUGHT;
+       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;
+       else if (ctx.n > 0)
+               ctx.arr[--ctx.n] = NULL;        /* Skip frame below __start */
+
+       return ctx.n;
+}