diff options
Diffstat (limited to 'libubacktrace')
| -rw-r--r-- | libubacktrace/Makefile | 14 | ||||
| -rw-r--r-- | libubacktrace/Makefile.in | 84 | ||||
| -rw-r--r-- | libubacktrace/arm/Makefile.arch | 17 | ||||
| -rw-r--r-- | libubacktrace/arm/backtrace.c | 98 | ||||
| -rw-r--r-- | libubacktrace/backtrace.c | 89 | ||||
| -rw-r--r-- | libubacktrace/backtracesyms.c | 104 | ||||
| -rw-r--r-- | libubacktrace/backtracesymsfd.c | 115 |
7 files changed, 521 insertions, 0 deletions
diff --git a/libubacktrace/Makefile b/libubacktrace/Makefile new file mode 100644 index 000000000..0f746383c --- /dev/null +++ b/libubacktrace/Makefile @@ -0,0 +1,14 @@ +# Makefile for uClibc (libubacktrace) +# +# Copyright (C) 2010 STMicroelectronics Ltd +# Author(s): Carmelo Amoroso <carmelo.amoroso@st.com> +# +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +top_srcdir=../ +top_builddir=../ +include $(top_builddir)Rules.mak +all: libs +include Makefile.in +include $(top_srcdir)Makerules diff --git a/libubacktrace/Makefile.in b/libubacktrace/Makefile.in new file mode 100644 index 000000000..311f1e31c --- /dev/null +++ b/libubacktrace/Makefile.in @@ -0,0 +1,84 @@ +# Makefile for uClibc (libubacktrace) +# +# Copyright (C) 2010 STMicroelectronics Ltd +# Author: Carmelo Amoroso <carmelo.amoroso@st.com> + +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +subdirs += libubacktrace libubacktrace/$(TARGET_ARCH) + +CFLAGS-libubacktrace := -DNOT_IN_libc -DIS_IN_libubacktrace $(SSP_ALL_CFLAGS) + +LDFLAGS-libubacktrace.so := $(LDFLAGS) $(top_builddir)lib/libdl-$(VERSION).so + +LIBS-libubacktrace.so := $(LIBS) + +libubacktrace_FULL_NAME := libubacktrace-$(VERSION).so + +libubacktrace_DIR := $(top_srcdir)libubacktrace +libubacktrace_OUT := $(top_builddir)libubacktrace + +libubacktrace_ARCH_DIR:=$(libubacktrace_DIR)/$(TARGET_ARCH) +libubacktrace_ARCH_OUT:=$(libubacktrace_OUT)/$(TARGET_ARCH) + +-include $(libubacktrace_ARCH_DIR)/Makefile.arch + +libubacktrace_SRC-y := +libubacktrace_SRC-$(UCLIBC_HAS_BACKTRACE) := backtracesyms.c backtracesymsfd.c +libubacktrace_SRC_SHARED-$(UCLIBC_HAS_BACKTRACE) := backtrace.c + +# remove generic sources, if arch specific version is present +ifneq ($(strip $(libubacktrace_ARCH_SRC-y)),) +libubacktrace_SRC-y := $(filter-out $(notdir $(libubacktrace_ARCH_SRC-y)),$(libubacktrace_SRC-y)) +libubacktrace_SRC_SHARED-y := $(filter-out $(notdir $(libubacktrace_ARCH_SRC-y)),$(libubacktrace_SRC_SHARED-y)) +endif + +# -fasynchronous-unwind-tables is required for backtrace to work using dwarf2 +CFLAGS-backtrace.c := -fasynchronous-unwind-tables + +libubacktrace_SRCS := $(patsubst %.c,$(libubacktrace_DIR)/%.c,$(libubacktrace_SRC-y)) +libubacktrace_OBJS := $(patsubst $(libubacktrace_DIR)/%.c,$(libubacktrace_OUT)/%.o,$(libubacktrace_SRCS)) + +libubacktrace_SHARED_SRCS := $(patsubst %.c,$(libubacktrace_DIR)/%.c,$(libubacktrace_SRC_SHARED-y)) +libubacktrace_SHARED_OBJS := $(patsubst $(libubacktrace_DIR)/%.c,$(libubacktrace_OUT)/%.o,$(libubacktrace_SHARED_SRCS)) + +libubacktrace-shared-y := $(libubacktrace_SHARED_OBJS:.o=.oS) +libubacktrace-static-y := $(libubacktrace_SHARED_OBJS) + +ifeq ($(DOPIC),y) +libubacktrace-a-y += $(libubacktrace_OBJS:.o=.os) $(libubacktrace-static-y:.o=.os) +else +libubacktrace-a-y += $(libubacktrace_OBJS) $(libubacktrace-static-y) +endif +libubacktrace-so-y += $(libubacktrace_OBJS:.o=.os) $(libubacktrace-shared-y) + + +lib-a-$(UCLIBC_HAS_BACKTRACE) += $(top_builddir)lib/libubacktrace.a +lib-so-$(UCLIBC_HAS_BACKTRACE) += $(top_builddir)lib/libubacktrace.so + +objclean-y += CLEAN_libubacktrace + +ifeq ($(DOMULTI),n) +$(top_builddir)lib/libubacktrace.so: $(libubacktrace_OUT)/libubacktrace_so.a $(libdl.depend) + $(call link.so,$(libubacktrace_FULL_NAME),$(ABI_VERSION)) +else +$(top_builddir)lib/libubacktrace.so: $(libubacktrace_OUT)/libubacktrace.oS | $(libdl.depend) + $(call linkm.so,$(libubacktrace_FULL_NAME),$(ABI_VERSION)) +endif + +$(libubacktrace_OUT)/libubacktrace_so.a: $(libubacktrace-so-y) + $(Q)$(RM) $@ + $(do_ar) + +$(libubacktrace_OUT)/libubacktrace.oS: $(libubacktrace_SRCS) $(libubacktrace_ARCH_SRCS) $(libubacktrace_SHARED_SRCS) + $(Q)$(RM) $@ + $(compile-m) + +$(top_builddir)lib/libubacktrace.a: $(libubacktrace-a-y) + $(Q)$(INSTALL) -d $(dir $@) + $(Q)$(RM) $@ + $(do_ar) + +CLEAN_libubacktrace: + $(do_rm) $(addprefix $(libubacktrace_OUT)/*., o os oS a) diff --git a/libubacktrace/arm/Makefile.arch b/libubacktrace/arm/Makefile.arch new file mode 100644 index 000000000..b3fb500e2 --- /dev/null +++ b/libubacktrace/arm/Makefile.arch @@ -0,0 +1,17 @@ +# Makefile for uClibc (libubacktrace) +# +# Author: Khem Raj <raj.khem@gmail.com> + +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +libubacktrace_ARCH_SRC-$(UCLIBC_HAS_BACKTRACE) := backtrace.c +libubacktrace_ARCH_SRCS := $(addprefix $(libubacktrace_ARCH_DIR)/,$(libubacktrace_ARCH_SRC-y)) +libubacktrace_ARCH_OBJS := $(patsubst $(libubacktrace_ARCH_DIR)/%.c,$(libubacktrace_ARCH_OUT)/%.o,$(libubacktrace_ARCH_SRCS)) + +ifeq ($(DOPIC),y) +libubacktrace-a-y+=$(libubacktrace_ARCH_OBJS:.o=.os) +else +libubacktrace-a-y+=$(libubacktrace_ARCH_OBJS) +endif +libubacktrace-so-y+=$(libubacktrace_ARCH_OBJS:.o=.oS) diff --git a/libubacktrace/arm/backtrace.c b/libubacktrace/arm/backtrace.c new file mode 100644 index 000000000..55689a741 --- /dev/null +++ b/libubacktrace/arm/backtrace.c @@ -0,0 +1,98 @@ +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + * User application that wants to use backtrace needs to be + * compiled with -fasynchronous-unwid-tables option and -rdynamic i + * to get full symbols printed. + * + * Author(s): Khem Raj <raj.khem@gmail.com> + * - ARM specific implementation of backtrace + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * + */ + +#include <libgcc_s.h> +#include <execinfo.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <unwind.h> +#include <assert.h> +#include <stdio.h> + +struct trace_arg +{ + void **array; + int cnt, size; +}; + +#ifdef SHARED +static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); +static _Unwind_VRS_Result (*unwind_vrs_get) (_Unwind_Context *, + _Unwind_VRS_RegClass, + _uw, + _Unwind_VRS_DataRepresentation, + void *); + +static void backtrace_init (void) +{ + void *handle = dlopen (LIBGCC_S_SO, RTLD_LAZY); + if (handle == NULL + || ((unwind_backtrace = dlsym (handle, "_Unwind_Backtrace")) == NULL) + || ((unwind_vrs_get = dlsym (handle, "_Unwind_VRS_Get")) == NULL)) { + printf(LIBGCC_S_SO " must be installed for backtrace to work\n"); + abort(); + } +} +#else +# define unwind_backtrace _Unwind_Backtrace +# define unwind_vrs_get _Unwind_VRS_Get +#endif +/* This function is identical to "_Unwind_GetGR", except that it uses + "unwind_vrs_get" instead of "_Unwind_VRS_Get". */ +static inline _Unwind_Word +unwind_getgr (_Unwind_Context *context, int regno) +{ + _uw val; + unwind_vrs_get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, &val); + return val; +} + +/* This macro is identical to the _Unwind_GetIP macro, except that it + uses "unwind_getgr" instead of "_Unwind_GetGR". */ +#define unwind_getip(context) \ + (unwind_getgr (context, 15) & ~(_Unwind_Word)1) + +static _Unwind_Reason_Code +backtrace_helper (struct _Unwind_Context *ctx, void *a) +{ + struct trace_arg *arg = a; + + assert (unwind_getip(ctx) != NULL); + + /* We are first called with address in the __backtrace function. Skip it. */ + if (arg->cnt != -1) + arg->array[arg->cnt] = (void *) unwind_getip (ctx); + if (++arg->cnt == arg->size) + return _URC_END_OF_STACK; + return _URC_NO_REASON; +} + +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + */ +int backtrace (void **array, int size) +{ + struct trace_arg arg = { .array = array, .size = size, .cnt = -1 }; + +#ifdef SHARED + if (unwind_backtrace == NULL) + backtrace_init(); +#endif + + if (size >= 1) + unwind_backtrace (backtrace_helper, &arg); + + return arg.cnt != -1 ? arg.cnt : 0; +} diff --git a/libubacktrace/backtrace.c b/libubacktrace/backtrace.c new file mode 100644 index 000000000..08a7010e7 --- /dev/null +++ b/libubacktrace/backtrace.c @@ -0,0 +1,89 @@ +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + * User application that wants to use backtrace needs to be + * compiled with -fasynchronous-unwind-tables option and -rdynamic to get full + * symbols printed. + * + * Copyright (C) 2009, 2010 STMicroelectronics Ltd. + * + * Author(s): Giuseppe Cavallaro <peppe.cavallaro@st.com> + * - Initial implementation for glibc + * + * Author(s): Carmelo Amoroso <carmelo.amoroso@st.com> + * - Reworked for uClibc + * - use dlsym/dlopen from libdl + * - rewrite initialisation to not use libc_once + * - make it available in static link too + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * + */ + +#include <libgcc_s.h> +#include <execinfo.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <unwind.h> +#include <assert.h> +#include <stdio.h> + +struct trace_arg +{ + void **array; + int cnt, size; +}; + +#ifdef SHARED +static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); +static _Unwind_Ptr (*unwind_getip) (struct _Unwind_Context *); + +static void backtrace_init (void) +{ + void *handle = dlopen (LIBGCC_S_SO, RTLD_LAZY); + + if (handle == NULL + || ((unwind_backtrace = dlsym (handle, "_Unwind_Backtrace")) == NULL) + || ((unwind_getip = dlsym (handle, "_Unwind_GetIP")) == NULL)) { + printf(LIBGCC_S_SO " must be installed for backtrace to work\n"); + abort(); + } +} +#else +# define unwind_backtrace _Unwind_Backtrace +# define unwind_getip _Unwind_GetIP +#endif + +static _Unwind_Reason_Code +backtrace_helper (struct _Unwind_Context *ctx, void *a) +{ + struct trace_arg *arg = a; + + assert (unwind_getip != NULL); + + /* We are first called with address in the __backtrace function. Skip it. */ + if (arg->cnt != -1) + arg->array[arg->cnt] = (void *) unwind_getip (ctx); + if (++arg->cnt == arg->size) + return _URC_END_OF_STACK; + return _URC_NO_REASON; +} + +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + */ +int backtrace (void **array, int size) +{ + struct trace_arg arg = { .array = array, .size = size, .cnt = -1 }; + +#ifdef SHARED + if (unwind_backtrace == NULL) + backtrace_init(); +#endif + + if (size >= 1) + unwind_backtrace (backtrace_helper, &arg); + + return arg.cnt != -1 ? arg.cnt : 0; +} diff --git a/libubacktrace/backtracesyms.c b/libubacktrace/backtracesyms.c new file mode 100644 index 000000000..10384dcd2 --- /dev/null +++ b/libubacktrace/backtracesyms.c @@ -0,0 +1,104 @@ +/* Return list with names for address in backtrace. + Copyright (C) 1998,1999,2000,2001,2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. + + Based on glibc/sysdeps/generic/elf/backtracesyms.c + + Copyright (C) 2010 STMicroelectronics Ltd + Author(s): Carmelo Amoroso <carmelo.amoroso@st.com> + * Modified to work with uClibc + - updated headers inclusion + - updated formatting and style + - updated to use dladdr from libdl */ + +#include <execinfo.h> +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <dlfcn.h> +#include <link.h> /* required for __ELF_NATIVE_CLASS */ + +#if __ELF_NATIVE_CLASS == 32 +# define WORD_WIDTH 8 +#else +/* We assyme 64bits. */ +# define WORD_WIDTH 16 +#endif + + +char ** backtrace_symbols (void *const *array, int size) +{ + Dl_info info[size]; + int status[size]; + int cnt; + size_t total = 0; + char **result; + + /* Fill in the information we can get from `dladdr'. */ + for (cnt = 0; cnt < size; ++cnt) { + status[cnt] = dladdr (array[cnt], &info[cnt]); + if (status[cnt] && info[cnt].dli_fname && + info[cnt].dli_fname[0] != '\0') + /* + * We have some info, compute the length of the string which will be + * "<file-name>(<sym-name>) [+offset]. + */ + total += (strlen (info[cnt].dli_fname ?: "") + + (info[cnt].dli_sname ? + strlen (info[cnt].dli_sname) + 3 + WORD_WIDTH + 3 : 1) + + WORD_WIDTH + 5); + else + total += 5 + WORD_WIDTH; + } + + /* Allocate memory for the result. */ + result = (char **) malloc (size * sizeof (char *) + total); + if (result != NULL) { + char *last = (char *) (result + size); + for (cnt = 0; cnt < size; ++cnt) { + result[cnt] = last; + + if (status[cnt] && info[cnt].dli_fname + && info[cnt].dli_fname[0] != '\0') { + + char buf[20]; + + if (array[cnt] >= (void *) info[cnt].dli_saddr) + sprintf (buf, "+%#lx", + (unsigned long)(array[cnt] - info[cnt].dli_saddr)); + else + sprintf (buf, "-%#lx", + (unsigned long)(info[cnt].dli_saddr - array[cnt])); + + last += 1 + sprintf (last, "%s%s%s%s%s[%p]", + info[cnt].dli_fname ?: "", + info[cnt].dli_sname ? "(" : "", + info[cnt].dli_sname ?: "", + info[cnt].dli_sname ? buf : "", + info[cnt].dli_sname ? ") " : " ", + array[cnt]); + } else + last += 1 + sprintf (last, "[%p]", array[cnt]); + } + assert (last <= (char *) result + size * sizeof (char *) + total); + } + + return result; +} diff --git a/libubacktrace/backtracesymsfd.c b/libubacktrace/backtracesymsfd.c new file mode 100644 index 000000000..498caa5c5 --- /dev/null +++ b/libubacktrace/backtracesymsfd.c @@ -0,0 +1,115 @@ +/* Write formatted list with names for addresses in backtrace to a file. + Copyright (C) 1998, 2000, 2003, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. + + Based on glibc/sysdeps/generic/elf/backtracesymsfd.c + + Copyright (C) 2010 STMicroelectronics Ltd + Author(s): Carmelo Amoroso <carmelo.amoroso@st.com> + * Modified to work with uClibc + - updated headers inclusion + - updated formatting and style + - updated to use dladdr from libdl + - updated to use snprintf instead of _itoa_word */ + +#include <execinfo.h> +#include <string.h> +#include <sys/uio.h> +#include <dlfcn.h> +#include <stdio.h> +#include <link.h> /* required for __ELF_NATIVE_CLASS */ + +#if __ELF_NATIVE_CLASS == 32 +# define WORD_WIDTH 8 +#else +/* We assyme 64bits. */ +# define WORD_WIDTH 16 +#endif + +#define BUF_SIZE (WORD_WIDTH + 1) + +void backtrace_symbols_fd (void *const *array, int size, int fd) +{ + struct iovec iov[9]; + int cnt; + + for (cnt = 0; cnt < size; ++cnt) { + char buf[BUF_SIZE]; + Dl_info info; + size_t last = 0; + size_t len = 0; + + memset(buf, 0, sizeof(buf)); + if (dladdr (array[cnt], &info) + && info.dli_fname && info.dli_fname[0] != '\0') { + /* Name of the file. */ + iov[0].iov_base = (void *) info.dli_fname; + iov[0].iov_len = strlen (info.dli_fname); + last = 1; + + /* Symbol name. */ + if (info.dli_sname != NULL) { + char buf2[BUF_SIZE]; + memset(buf2, 0, sizeof(buf2)); + size_t diff; + + iov[1].iov_base = (void *) "("; + iov[1].iov_len = 1; + iov[2].iov_base = (void *) info.dli_sname; + iov[2].iov_len = strlen (info.dli_sname); + + if (array[cnt] >= (void *) info.dli_saddr) { + iov[3].iov_base = (void *) "+0x"; + diff = array[cnt] - info.dli_saddr; + } else { + iov[3].iov_base = (void *) "-0x"; + diff = info.dli_saddr - array[cnt]; + } + + iov[3].iov_len = 3; + + /* convert diff to a string in hex format */ + len = snprintf(buf2, sizeof(buf2), "%lx", (unsigned long) diff); + iov[4].iov_base = buf2; + iov[4].iov_len = len; + + iov[5].iov_base = (void *) ")"; + iov[5].iov_len = 1; + + last = 6; + } + } + + iov[last].iov_base = (void *) "[0x"; + iov[last].iov_len = 3; + ++last; + + /* convert array[cnt] to a string in hex format */ + len = snprintf(buf, sizeof(buf), "%lx", (unsigned long) array[cnt]); + iov[last].iov_base = buf; + iov[last].iov_len = len; + + ++last; + + iov[last].iov_base = (void *) "]\n"; + iov[last].iov_len = 2; + ++last; + + writev (fd, iov, last); + } +} |
