From 37eb913ed8c4798b736e678f4dbd9f4a91a68f74 Mon Sep 17 00:00:00 2001 From: Salvatore Cro Date: Thu, 9 Sep 2010 15:45:44 +0200 Subject: libubacktrace: Provide uClibc with backtrace functions A new shared object, libubacktrace.so.0 is added to uClibc to provide backtrace functions to support application self-debugging. This set of functions requires to dynamically load libgcc_s.so so they need to call dlopen/dlsym that are provided by libdl. For this reason they cannot be included into libc.so.0 but are provided by a new library. User application that wants to use backtrace needs to be compiled with -fexceptions option and -rdynamic to get full symbols printed and must be linked against libubacktrace.so Signed-off-by: Carmelo Amoroso --- libubacktrace/Makefile | 14 ++++ libubacktrace/Makefile.in | 84 ++++++++++++++++++++++++ libubacktrace/backtrace.c | 19 ++++++ libubacktrace/backtracesyms.c | 105 +++++++++++++++++++++++++++++ libubacktrace/backtracesymsfd.c | 116 +++++++++++++++++++++++++++++++++ libubacktrace/sysdeps/sh/Makefile.arch | 12 ++++ libubacktrace/sysdeps/sh/backtrace.c | 84 ++++++++++++++++++++++++ 7 files changed, 434 insertions(+) create mode 100644 libubacktrace/Makefile create mode 100644 libubacktrace/Makefile.in create mode 100644 libubacktrace/backtrace.c create mode 100644 libubacktrace/backtracesyms.c create mode 100644 libubacktrace/backtracesymsfd.c create mode 100644 libubacktrace/sysdeps/sh/Makefile.arch create mode 100644 libubacktrace/sysdeps/sh/backtrace.c (limited to 'libubacktrace') 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 +# +# 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..c1dd5d7ab --- /dev/null +++ b/libubacktrace/Makefile.in @@ -0,0 +1,84 @@ +# Makefile for uClibc (libubacktrace) +# +# Copyright (C) 2010 STMicroelectronics Ltd +# Author: Carmelo Amoroso + +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +subdirs += libubacktrace + +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)/sysdeps/$(TARGET_ARCH) +libubacktrace_ARCH_OUT := $(libubacktrace_OUT)/sysdeps/$(TARGET_ARCH) + +-include $(libubacktrace_ARCH_DIR)/Makefile.arch + +libubacktrace_SRC-y := +libubacktrace_SRC-$(UCLIBC_HAS_BACKTRACE) := backtrace.c backtracesyms.c backtracesymsfd.c + +CFLAGS-libubacktrace/sysdeps/$(TARGET_ARCH)/ := $(CFLAGS-libubacktrace) + +# 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_ARCH_SRC := $(addprefix $(libubacktrace_ARCH_DIR)/,$(libubacktrace_ARCH_SRC-y)) +libubacktrace_ARCH_OBJ := $(patsubst $(libubacktrace_ARCH_DIR)/%.c,$(libubacktrace_ARCH_OUT)/%.o,$(libubacktrace_ARCH_SRC)) +endif + + +libubacktrace_SRC := $(addprefix $(libubacktrace_DIR)/,$(libubacktrace_SRC-y)) +libubacktrace_OBJ := $(patsubst $(libubacktrace_DIR)/%.c,$(libubacktrace_OUT)/%.o,$(libubacktrace_SRC)) + +libubacktrace_SRCS := $(libubacktrace_SRC) $(libubacktrace_ARCH_SRC) +libubacktrace_OBJS := $(libubacktrace_OBJ) $(libubacktrace_ARCH_OBJ) + +ifeq ($(DOPIC),y) +libubacktrace-a-y := $(libubacktrace_OBJS:.o=.os) +else +libubacktrace-a-y := $(libubacktrace_OBJS) +endif +libubacktrace-so-y := $(libubacktrace_OBJS:.o=.os) + +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) +ifeq ($(DOPIC),y) +$(top_builddir)lib/libubacktrace.so: $(top_builddir)lib/libubacktrace.a $(libdl.depend) +else +$(top_builddir)lib/libubacktrace.so: $(libubacktrace_OUT)/libubacktrace_so.a $(libdl.depend) +endif + $(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) + $(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) \ + $(addprefix $(libubacktrace_ARCH_OUT)/*., o os oS a) diff --git a/libubacktrace/backtrace.c b/libubacktrace/backtrace.c new file mode 100644 index 000000000..872180028 --- /dev/null +++ b/libubacktrace/backtrace.c @@ -0,0 +1,19 @@ +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + * User application that wants to use backtrace needs to be + * compiled with -fexceptions option and -rdynamic to get full + * symbols printed. + + * Copyright (C) 2010 STMicroelectronics Ltd + * Author(s): Carmelo Amoroso + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * + */ +#error "Arch specific implementation must be provided to properly work" +int backtrace (void **array, int size) +{ + return -1; +} + diff --git a/libubacktrace/backtracesyms.c b/libubacktrace/backtracesyms.c new file mode 100644 index 000000000..4486fee94 --- /dev/null +++ b/libubacktrace/backtracesyms.c @@ -0,0 +1,105 @@ +/* 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 , 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Based on glibc/sysdeps/generic/elf/backtracesyms.c + + Copyright (C) 2010 STMicroelectronics Ltd + Author(s): Carmelo Amoroso + * Modified to work with uClibc + - updated headers inclusion + - updated formatting and style + - updated to use dladdr from libdl */ + +#include +#include +#include +#include +#include + +#include +#include /* 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 + * "() [+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..66d7687ca --- /dev/null +++ b/libubacktrace/backtracesymsfd.c @@ -0,0 +1,116 @@ +/* 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 , 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Based on glibc/sysdeps/generic/elf/backtracesymsfd.c + + Copyright (C) 2010 STMicroelectronics Ltd + Author(s): Carmelo Amoroso + * 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 +#include +#include +#include +#include +#include /* 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); + } +} diff --git a/libubacktrace/sysdeps/sh/Makefile.arch b/libubacktrace/sysdeps/sh/Makefile.arch new file mode 100644 index 000000000..9b0de385b --- /dev/null +++ b/libubacktrace/sysdeps/sh/Makefile.arch @@ -0,0 +1,12 @@ +# Makefile for uClibc (sh/libubacktrace) +# +# Copyright (C) 2010 STMicroelectronics Ltd +# Author: Carmelo Amoroso + +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +libubacktrace_ARCH_SRC-y := backtrace.c + +# -fexections is required for backtrace to work using dwarf2 +CFLAGS-backtrace.c := -fexceptions diff --git a/libubacktrace/sysdeps/sh/backtrace.c b/libubacktrace/sysdeps/sh/backtrace.c new file mode 100644 index 000000000..18b91b1bb --- /dev/null +++ b/libubacktrace/sysdeps/sh/backtrace.c @@ -0,0 +1,84 @@ +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + * User application that wants to use backtrace needs to be + * compiled with -fexceptions option and -rdynamic to get full + * symbols printed. + * + * Copyright (C) 2009, 2010 STMicroelectronics Ltd. + * + * Author(s): Giuseppe Cavallaro + * - Initial implementation for glibc + * + * Author(s): Carmelo Amoroso + * - 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 +#include +#include +#include +#include +#include + +struct trace_arg +{ + void **array; + int cnt, size; +}; + +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.1", RTLD_LAZY); + + if (handle == NULL + || ((unwind_backtrace = dlsym (handle, "_Unwind_Backtrace")) == NULL) + || ((unwind_getip = dlsym (handle, "_Unwind_GetIP")) == NULL)) { + printf("libgcc_s.so.1 must be installed for backtrace to work\n"); + abort(); + } +} + +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. + * + * User application that wants to use backtrace needs to be + * compiled with -fexceptions option and -rdynamic to get full + * symbols printed. + */ +int backtrace (void **array, int size) +{ + struct trace_arg arg = { .array = array, .size = size, .cnt = -1 }; + + if (unwind_backtrace == NULL) + backtrace_init(); + + if (size >= 1) + unwind_backtrace (backtrace_helper, &arg); + + return arg.cnt != -1 ? arg.cnt : 0; +} -- cgit v1.2.3