summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-10-18 09:10:05 +0200
committerJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-11-24 21:20:27 +0100
commitfa668f3b96fd0363bae1adc640105b3914faa138 (patch)
tree25148826f7a96db52d9e806def1e1a409be0fddd
parentc6bb0e49bed67c0cd53cd725f5efa09e6e94df56 (diff)
downloadlatrace-fa668f3b96fd0363bae1adc640105b3914faa138.tar.gz
latrace-fa668f3b96fd0363bae1adc640105b3914faa138.tar.xz
latrace-fa668f3b96fd0363bae1adc640105b3914faa138.zip
error simulation: added support to display backtrace info
-rw-r--r--configure.ac29
-rw-r--r--src/Makefile18
-rw-r--r--src/audit-error.c51
-rw-r--r--src/audit.c26
-rw-r--r--src/autoconf.h.in6
-rw-r--r--src/autoconf.make.in3
-rw-r--r--src/backtrace-fp.c10
-rw-r--r--src/backtrace-lunw.c76
-rw-r--r--src/backtrace.c390
-rw-r--r--src/backtrace.h46
-rw-r--r--src/config.h4
-rw-r--r--src/run.c4
-rw-r--r--src/sysdeps/i686/error.c7
-rw-r--r--src/sysdeps/x86_64/error.c7
14 files changed, 643 insertions, 34 deletions
diff --git a/configure.ac b/configure.ac
index aa80b3a..7ac3f17 100644
--- a/configure.ac
+++ b/configure.ac
@@ -112,8 +112,35 @@ else
AC_MSG_WARN([Error simulation support disabled])
fi
+# check for libunwind, haven't found any generic function,
+# so need to set one based on architecture
+unwind_func="krava"
+if test "$unamem" = "x86_64"; then
+ unwind_func="_Ux86_64_init_local"
+fi
+if test "$unamem" = "i686"; then
+ unwind_func="_Ux86_init_local"
+fi
+
+AC_SEARCH_LIBS([$unwind_func], [unwind-generic],
+ [
+ AC_DEFINE(CONFIG_LIBUNWIND, 1, "Unwind library found.")
+ AC_SUBST(CONFIG_LIBUNWIND, "y")
+ ],
+ [AC_MSG_WARN([libunwind not found, no backtrace support (install libunwind-dev)])])
+
+AC_SEARCH_LIBS([elf_begin], [elf],
+ [
+ AC_DEFINE(CONFIG_LIBELF, 1, "Elf library found.")
+ AC_SUBST(CONFIG_LIBELF, "y")
+ ],
+ [AC_MSG_WARN([libelf not found, no symbol support (install libelf-dev)])])
+
AC_SEARCH_LIBS([cplus_demangle], [iberty_pic iberty],
- [AC_DEFINE(CONFIG_LIBERTY, 1, "Liberty found.")],
+ [
+ AC_DEFINE(CONFIG_LIBERTY, 1, "Liberty library found.")
+ AC_SUBST(CONFIG_LIBERTY, "y")
+ ],
[AC_MSG_WARN([libiberty not found, no demangle support (install binutils-dev)])])
AC_CONFIG_HEADER([src/autoconf.h])
diff --git a/src/Makefile b/src/Makefile
index e009358..181ab8e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -40,12 +40,18 @@ AUDIT_OBJS+=\
src/sysdeps/$(CONFIG_SYSDEP_DIR)/stack.o
endif
-# TODO make this for all error simulation objects
ifeq ($(CONFIG_ARCH_HAVE_ERROR_SIM),y)
AUDIT_OBJS+=\
src/sysdeps/$(CONFIG_SYSDEP_DIR)/error.o \
- src/audit-error.o
-endif
+ src/audit-error.o \
+ src/backtrace.o
+ifeq ($(CONFIG_LIBUNWIND),y)
+AUDIT_OBJS+=\
+ src/backtrace-lunw.o
+else
+ src/backtrace-fp.o
+endif # CONFIG_LIBUNWIND
+endif # CONFIG_ARCH_HAVE_ERROR_SIM
OBJS+=$(AUDIT_OBJS)
PROGRAMS+= $(AUDIT_BIN)
@@ -63,7 +69,11 @@ install::
# latrace binary
LATRACE_BIN=latrace
LATRACE_CTL=latrace-ctl
-LATRACE_LIB= $(LIBS)
+LATRACE_LIB=
+
+ifeq ($(CONFIG_LIBERTY),y)
+LATRACE_LIB+= -liberty
+endif
LATRACE_OBJS=\
src/latrace.o \
diff --git a/src/audit-error.c b/src/audit-error.c
index 6a9bc50..428d41e 100644
--- a/src/audit-error.c
+++ b/src/audit-error.c
@@ -8,6 +8,7 @@
#include <string.h>
#include "config.h"
+#include "backtrace.h"
int lt_audit_parse_init(struct lt_config_shared *cfg);
int lt_audit_parse();
@@ -43,14 +44,43 @@ struct lt_error_sym* lt_error_sym_get(struct lt_config_shared *cfg,
extern __thread int pipe_fd;
+static void share_info(struct lt_config_audit *cfg,
+ struct lt_error_sym *esym,
+ struct timeval *tv,
+ struct link_map *lr,
+ const La_regs *inregs)
+{
+ void *ret_addr;
+#define LT_MAXMSG 100
+ char text[LT_MAXMSG];
+
+ lt_error_get_retaddr(cfg, &ret_addr, inregs);
+ ret_addr -= lr->l_addr;
+
+ snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld, ret %p",
+ esym->ret, ret_addr);
+
+ if (lt_sh(cfg, pipe)) {
+ char buf[LT_FIFO_MSG_MAXLEN];
+ int len;
+
+ len = lt_fifo_mtext_get(cfg->sh, buf, tv, text);
+ /* TODO erro handling */
+ lt_fifo_send(cfg->sh, pipe_fd, buf, len);
+ } else
+ lt_out_text(cfg->sh, tv, syscall(SYS_gettid), text);
+
+ dump_backtrace(cfg->sh, 0);
+}
+
int lt_error_sym_exit(struct lt_config_audit *cfg,
struct lt_symbol *sym,
struct timeval *tv,
+ struct link_map *lr,
+ const La_regs *inregs,
La_retval *outregs)
{
struct lt_error_sym *esym;
-#define LT_MAXMSG 100
- char text[LT_MAXMSG];
/* we're not interested in this symbol */
if (!sym || !sym->error)
@@ -69,22 +99,11 @@ int lt_error_sym_exit(struct lt_config_audit *cfg,
esym->ret);
/* The fun begins right now.. let's change the return
- * value for the symbol */
-
+ * value for the symbol ... */
lt_error_set_retval(cfg, esym->ret, outregs);
- snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld",
- esym->ret);
-
- if (lt_sh(cfg, pipe)) {
- char buf[LT_FIFO_MSG_MAXLEN];
- int len;
-
- len = lt_fifo_mtext_get(cfg, buf, tv, text);
- return lt_fifo_send(cfg, pipe_fd, buf, len);
- }
-
- lt_out_text(cfg->sh, tv, syscall(SYS_gettid), text);
+ /* and share some info about the sinner */
+ share_info(cfg, esym, tv, lr, inregs);
return 0;
}
diff --git a/src/audit.c b/src/audit.c
index 3adfa3e..7a83b56 100644
--- a/src/audit.c
+++ b/src/audit.c
@@ -81,9 +81,11 @@ static void free_argbuf(int argret, char *argbuf, char *argdbuf)
}
static int sym_entry(const char *symname, void *ptr,
- char *lib_from, char *lib_to, La_regs *regs)
+ struct link_map *lr, struct link_map *ld,
+ La_regs *regs)
{
- int argret = -1;
+ char *lib_to = ld ? ld->l_name : NULL;
+ int argret = -1;
char *argbuf = "", *argdbuf = "";
struct timeval tv;
struct lt_symbol *sym = NULL;
@@ -130,10 +132,12 @@ static int sym_entry(const char *symname, void *ptr,
}
static int sym_exit(const char *symname, void *ptr,
- char *lib_from, char *lib_to,
- const La_regs *inregs, La_retval *outregs)
+ struct link_map *lr, struct link_map *ld,
+ const La_regs *inregs, La_retval *outregs)
{
- int argret = -1;
+ char *lib_from = lr ? lr->l_name : NULL;
+ char *lib_to = ld ? ld->l_name : NULL;
+ int argret = -1;
char *argbuf = "", *argdbuf = "";
struct timeval tv;
struct lt_symbol *sym = NULL;
@@ -149,8 +153,10 @@ static int sym_exit(const char *symname, void *ptr,
if (lt_sh(&cfg, global_symbols))
sym = lt_symbol_get(cfg.sh, ptr, symname);
+#ifdef CONFIG_ARCH_HAVE_ERROR_SIM
if (lt_sh(&cfg, error_sim))
- lt_error_sym_exit(&cfg, sym, &tv, outregs);
+ lt_error_sym_exit(&cfg, sym, &tv, lr, inregs, outregs);
+#endif
#ifdef CONFIG_ARCH_HAVE_ARGS
argret = lt_sh(&cfg, args_enabled) ?
@@ -329,9 +335,7 @@ pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
CHECK_PID(sym->st_value);
sym_entry(symname, (void*) sym->st_value,
- lr ? lr->l_name : NULL,
- ld ? ld->l_name : NULL,
- regs);
+ lr, ld, regs);
} while(0);
@@ -351,9 +355,7 @@ unsigned int pltexit(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
CHECK_PID(0);
sym_exit(symname, (void*) sym->st_value,
- lr ? lr->l_name : NULL,
- ld ? ld->l_name : NULL,
- inregs, outregs);
+ lr, ld, inregs, outregs);
} while(0);
diff --git a/src/autoconf.h.in b/src/autoconf.h.in
index d2f53fb..4b77ab6 100644
--- a/src/autoconf.h.in
+++ b/src/autoconf.h.in
@@ -36,6 +36,12 @@
/* liberty */
#undef CONFIG_LIBERTY
+/* libunwind */
+#undef CONFIG_LIBUNWIND
+
+/* libelf */
+#undef CONFIG_LIBELF
+
/* large file support */
#undef _LARGE_FILES
#undef _FILE_OFFSET_BITS
diff --git a/src/autoconf.make.in b/src/autoconf.make.in
index 9655f4a..dac8a8b 100644
--- a/src/autoconf.make.in
+++ b/src/autoconf.make.in
@@ -47,5 +47,8 @@ CONFIG_SYSDEP_DIR = @CONFIG_SYSDEP_DIR@
CONFIG_VERSION = @CONFIG_VERSION@
CONFIG_ARCH_HAVE_ARGS = @CONFIG_ARCH_HAVE_ARGS@
CONFIG_ARCH_HAVE_TEST = @CONFIG_ARCH_HAVE_TEST@
+CONFIG_LIBUNWIND = @CONFIG_LIBUNWIND@
+CONFIG_LIBELF = @CONFIG_LIBELF@
+CONFIG_LIBERTY = @CONFIG_LIBERTY@
CONFIG_ARCH_HAVE_ERROR_SIM = @CONFIG_ARCH_HAVE_ERROR_SIM@
diff --git a/src/backtrace-fp.c b/src/backtrace-fp.c
new file mode 100644
index 0000000..1c70dfc
--- /dev/null
+++ b/src/backtrace-fp.c
@@ -0,0 +1,10 @@
+
+#include "config.h"
+#include "backtrace.h"
+
+int bt_dump(struct lt_config_shared *cfg, unsigned long flags)
+{
+ fprintf(stderr, "NOT IMPLEMENTED\n");
+ fflush(NULL);
+ return -1;
+}
diff --git a/src/backtrace-lunw.c b/src/backtrace-lunw.c
new file mode 100644
index 0000000..eb29467
--- /dev/null
+++ b/src/backtrace-lunw.c
@@ -0,0 +1,76 @@
+#include <libunwind.h>
+
+#include "config.h"
+#include "backtrace.h"
+
+#define LT_MAXMSG 300
+
+int bt_dump(struct lt_config_shared *cfg, unsigned long flags)
+{
+ unw_cursor_t cursor;
+ unw_context_t uc;
+ int ret = 0;
+
+ unw_getcontext(&uc);
+ if (unw_init_local(&cursor, &uc) < 0) {
+ bt_display(cfg, "unw_init_local failed");
+ return -1;
+ }
+
+ bt_display(cfg, "backtrace:");
+
+ do {
+ char text[LT_MAXMSG];
+ struct bt_map *map;
+ bt_word_t rel_ip = 0, ip = 0, sp;
+ bt_word_t sym_offset = 0;
+ bt_word_t sym_size= 0;
+ char *map_name = NULL;
+ char *sym_name = NULL;
+ struct bt_symbol *symbol = NULL;
+ int size = sizeof(bt_word_t) * 2;
+ int symbol_found = 0;
+
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ unw_get_reg(&cursor, UNW_REG_SP, &sp);
+
+ if (bt_find_map(cfg, &map, ip)) {
+ rel_ip = ip - map->base;
+ map_name = map->name;
+
+ if (bt_find_symbol(cfg, &symbol, map, rel_ip)) {
+ sym_name = symbol->name;
+ sym_offset = rel_ip - symbol->start;
+ sym_size = symbol->end - symbol->start;
+ symbol_found = 1;
+ }
+ }
+
+ if (symbol_found)
+ snprintf(text, LT_MAXMSG,
+ "\t<0x%0*zx> %s+0x%zx/0x%zx [%s]",
+ size, rel_ip,
+ sym_name,
+ sym_offset,
+ sym_size,
+ map_name);
+ else
+ snprintf(text, LT_MAXMSG,
+ "\t<0x%0*zx> [%s]",
+ size, rel_ip,
+ map_name);
+
+ bt_display(cfg, text);
+
+ ret = unw_step(&cursor);
+ if (ret < 0) {
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ snprintf(text, LT_MAXMSG,
+ "unw_step failed for ip=%zx\n",
+ ip);
+ bt_display(cfg, text);
+ }
+ } while (ret > 0);
+
+ return ret;
+}
diff --git a/src/backtrace.c b/src/backtrace.c
new file mode 100644
index 0000000..fb9c060
--- /dev/null
+++ b/src/backtrace.c
@@ -0,0 +1,390 @@
+#include <link.h>
+#include <elf.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "config.h"
+#include "backtrace.h"
+
+extern __thread int pipe_fd;
+
+static struct bt_map* maps;
+
+#ifdef CONFIG_LIBELF
+static void symbols_destroy(struct bt_map *map)
+{
+ struct bt_symbol* next = map->symbols;
+
+ do {
+ struct bt_symbol *symbol = next;
+ next = symbol->next;
+ free(symbol->name);
+ free(symbol);
+ } while(next);
+}
+
+static Elf_Scn *get_section(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name,
+ size_t *idx)
+{
+ Elf_Scn *sec = NULL;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str)) {
+ if (idx)
+ *idx = cnt;
+ break;
+ }
+ ++cnt;
+ }
+
+ return sec;
+}
+
+static int sym_type(GElf_Sym *sym)
+{
+ return GELF_ST_TYPE(sym->st_info);
+}
+
+static int sym_is_function(GElf_Sym *sym)
+{
+ return sym_type(sym) == STT_FUNC &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static int sym_is_label(GElf_Sym *sym)
+{
+ return sym_type(sym) == STT_NOTYPE &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx != SHN_ABS;
+}
+
+static char *sec_name(GElf_Shdr *shdr, Elf_Data *secstrs)
+{
+ return secstrs->d_buf + shdr->sh_name;
+}
+
+static char *sym_name(GElf_Sym *sym, Elf_Data *symstrs)
+{
+ return symstrs->d_buf + sym->st_name;
+}
+
+static struct bt_symbol* __get_symbols(struct lt_config_shared *cfg,
+ Elf *elf, GElf_Ehdr *ehdr,
+ Elf_Scn *sec, GElf_Shdr *shdr)
+{
+ Elf_Data *syms, *symstrs, *secstrs;
+ Elf_Scn *sec_str, *sec_strndx;
+ GElf_Sym sym;
+ struct bt_symbol *sym_last = NULL;
+ uint32_t idx, nr_syms;
+
+ syms = elf_getdata(sec, NULL);
+ if (!syms) {
+ PRINT_VERBOSE(cfg, 1, "elf_getdata failed\n");
+ return NULL;
+ }
+
+ sec_str = elf_getscn(elf, shdr->sh_link);
+ if (!sec_str) {
+ PRINT_VERBOSE(cfg, 1, "no strtab\n");
+ return NULL;
+ }
+
+ symstrs = elf_getdata(sec_str, NULL);
+ if (!symstrs) {
+ PRINT_VERBOSE(cfg, 1, "no strtab\n");
+ return NULL;
+ }
+
+ sec_strndx = elf_getscn(elf, ehdr->e_shstrndx);
+ if (!sec_strndx) {
+ PRINT_VERBOSE(cfg, 1, "no strndx\n");
+ return NULL;
+ }
+
+ secstrs = elf_getdata(sec_strndx, NULL);
+ if (!secstrs) {
+ PRINT_VERBOSE(cfg, 1, "no strndx data\n");
+ return NULL;
+ }
+
+ nr_syms = shdr->sh_size / shdr->sh_entsize;
+
+ for (idx = 0, gelf_getsym(syms, idx, &sym);
+ idx < nr_syms;
+ idx++, gelf_getsym(syms, idx, &sym)) {
+ Elf_Scn *sec_code;
+ GElf_Shdr shdr_code;
+ struct bt_symbol *symbol;
+
+ if (!sym_is_label(&sym) && !sym_is_function(&sym))
+ continue;
+
+ sec_code = elf_getscn(elf, sym.st_shndx);
+ if (!sec_code) {
+ PRINT_VERBOSE(cfg, 1, "failed to get symbol section\n");
+ return NULL;
+ }
+
+ gelf_getshdr(sec_code, &shdr_code);
+
+ if (!strstr(sec_name(&shdr_code, secstrs), "text")) {
+ PRINT_VERBOSE(cfg, 1, "text section expected, got '%s'\n",
+ sec_name(&shdr_code, secstrs));
+ continue;
+ }
+
+ symbol = malloc(sizeof(*symbol));
+ if (!symbol) {
+ PRINT_VERBOSE(cfg, 1, "failed to allocate symbol\n");
+ return NULL;
+ }
+
+ symbol->start = sym.st_value;
+ symbol->end = symbol->start + sym.st_size;
+ symbol->name = strdup(sym_name(&sym, symstrs));
+ symbol->next = sym_last;
+
+ sym_last = symbol;
+
+ PRINT_VERBOSE(cfg, 1, "adding symbol %s, %lx, %lx\n",
+ symbol->name, symbol->start, symbol->end);
+ }
+
+ return sym_last;
+}
+
+static struct bt_symbol* get_symbols(struct lt_config_shared *cfg,
+ int fd, struct bt_map *map)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr_sym, shdr_dyn;
+ Elf_Scn *sec_sym, *sec_dyn;
+ struct bt_symbol *s = NULL;
+
+ PRINT_VERBOSE(cfg, 1, "map1 %s\n", map->name);
+
+ elf_version(EV_CURRENT);
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (!elf) {
+ PRINT_VERBOSE(cfg, 1, "elf_begin failed: %s\n",
+ elf_errmsg(elf_errno()));
+ return NULL;
+ }
+
+ PRINT_VERBOSE(cfg, 1, "map2 %s\n", map->name);
+
+ do {
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ PRINT_VERBOSE(cfg, 1, "gelf_getehdr failed\n");
+ break;
+ }
+
+ sec_sym = get_section(elf, &ehdr, &shdr_sym, ".symtab", NULL);
+ sec_dyn = get_section(elf, &ehdr, &shdr_dyn, ".dynsym", NULL);
+
+ if (!sec_sym && !sec_dyn) {
+ PRINT_VERBOSE(cfg, 1, "no symbols\n");
+ break;
+ }
+
+ PRINT_VERBOSE(cfg, 1, "map %s\n", map->name);
+
+ if (sec_sym)
+ s = __get_symbols(cfg, elf, &ehdr,
+ sec_sym, &shdr_sym);
+
+ map->symbols = s;
+
+ if (sec_dyn) {
+ s = __get_symbols(cfg, elf, &ehdr,
+ sec_dyn, &shdr_dyn);
+ if (s) {
+ s->next = map->symbols;
+ map->symbols = s;
+ }
+ }
+ PRINT_VERBOSE(cfg, 1, "map %s, symbols %p\n", map->name, map->symbols);
+ } while(0);
+
+ elf_end(elf);
+ return s;
+}
+
+static int symbols_init(struct lt_config_shared *cfg, struct bt_map *map)
+{
+ int fd;
+
+ fd = open(map->name, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ map->symbols = get_symbols(cfg, fd, map);
+
+ close(fd);
+
+ return map->symbols ? 0 : -1;
+}
+
+int bt_find_symbol(struct lt_config_shared *cfg, struct bt_symbol **sym,
+ struct bt_map *map, bt_word_t ip)
+{
+ struct bt_symbol* symbol = map->symbols;
+
+ PRINT_VERBOSE(cfg, 1, "looking for ip %lx, %p, %p\n", ip, map, symbol);
+
+ while(symbol) {
+ PRINT_VERBOSE(cfg, 1, "looking for ip %lx, start %lx, size %lx, name %s\n",
+ ip, symbol->start, symbol->end, symbol->name);
+
+ if (ip >= symbol->start && ip <= symbol->end) {
+ *sym = symbol;
+ return 1;
+ }
+ symbol = symbol->next;
+ }
+
+ PRINT_VERBOSE(cfg, 1, "looking for ip %lx, %s\n", ip, map->name);
+ return 0;
+}
+#else
+int bt_find_symbol(struct lt_config_shared *cfg, struct bt_symbol **sym,
+ struct bt_map *map, bt_word_t ip)
+{
+ return 0;
+}
+
+static int symbols_init(struct lt_config_shared *cfg, struct bt_map *map)
+{
+ return 0;
+}
+
+static void symbols_destroy(struct bt_map *map) { }
+#endif /* CONFIG_LIBELF */
+
+int bt_find_map(struct lt_config_shared *cfg, struct bt_map **map,
+ bt_word_t ip)
+{
+ struct bt_map* m = maps;
+
+ PRINT_VERBOSE(cfg, 1, "ip %lx\n", ip);
+
+ while(m) {
+ PRINT_VERBOSE(cfg, 1, "ip %lx, start %lx. end %lx\n", ip, m->start, m->end);
+ if (ip >= m->start && ip <= m->end) {
+ PRINT_VERBOSE(cfg, 1, "found map %p\n", m);
+ *map = m;
+ return 1;
+ }
+ m = m->next;
+ }
+
+ PRINT_VERBOSE(cfg, 1, "not found\n");
+ return 0;
+}
+
+/* TODO erro handling */
+void bt_display(struct lt_config_shared *cfg, char *text)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ if (lt_sh(cfg, pipe)) {
+ char buf[LT_FIFO_MSG_MAXLEN];
+ int len;
+
+ len = lt_fifo_mtext_get(cfg, buf, &tv, text);
+ lt_fifo_send(cfg, pipe_fd, buf, len);
+ } else
+ lt_out_text(cfg, &tv, syscall(SYS_gettid), text);
+}
+
+static int maps_callback(struct dl_phdr_info *info,
+ size_t size, void *data)
+{
+ struct lt_config_shared *cfg = data;
+ const ElfW(Phdr) *phdr = info->dlpi_phdr;
+ bt_word_t load_base = info->dlpi_addr;
+ long n;
+
+ for (n = info->dlpi_phnum; --n >= 0; phdr++) {
+ struct bt_map *map;
+
+ if ((phdr->p_type != PT_LOAD) ||
+ (!(phdr->p_flags & PF_X)))
+ continue;
+
+ map = malloc(sizeof(*map));
+ if (!map)
+ return -1;
+
+ map->base = load_base;
+ map->start = phdr->p_vaddr + load_base;
+ map->end = map->start + phdr->p_memsz;
+ map->name = (char*) info->dlpi_name;
+ map->symbols = NULL;
+
+ map->next = maps;
+ maps = map;
+
+ if (symbols_init(cfg, map))
+ PRINT_VERBOSE(cfg, 1,
+ "failed to build symbols for %s\n",
+ map->name);
+
+ PRINT_VERBOSE(cfg, 1, "adding map %p, %p - %p @ %s\n",
+ map, map->start, map->end, map->name);
+ }
+
+ return 0;
+}
+
+static int maps_init(struct lt_config_shared *cfg)
+{
+ return dl_iterate_phdr(maps_callback, cfg);
+}
+
+static void maps_destroy(struct lt_config_shared *cfg)
+{
+ struct bt_map* next = maps;
+
+ do {
+ struct bt_map *map = next;
+ next = map->next;
+
+ symbols_destroy(map);
+ free(map);
+ } while(next);
+}
+
+void dump_backtrace(struct lt_config_shared *cfg, unsigned long flags)
+{
+ /* moving on even if we fail here */
+ if (maps_init(cfg))
+ bt_display(cfg, "failed to build maps");
+
+ if (bt_dump(cfg, flags))
+ bt_display(cfg, "dump failed");
+
+ if (BT_FLAG_DESTROY && flags)
+ maps_destroy(cfg);
+
+ /* empty line after the backtrace */
+ bt_display(cfg, "");
+}
diff --git a/src/backtrace.h b/src/backtrace.h
new file mode 100644
index 0000000..1ba10f0
--- /dev/null
+++ b/src/backtrace.h
@@ -0,0 +1,46 @@
+#ifndef BACKTRACE_H
+#define BACKTRACE_H
+
+#define BIT(x) (1 << x)
+
+enum {
+ BT_FLAG_DESTROY = BIT(0),
+};
+
+#if defined(__x86_64)
+typedef uint64_t bt_word_t;
+#else
+typedef uint32_t bt_word_t;
+#endif
+
+struct bt_symbol;
+
+struct bt_map {
+ bt_word_t base;
+ bt_word_t start;
+ bt_word_t end;
+ char *name;
+ struct bt_symbol *symbols;
+ struct bt_map *next;
+};
+
+struct bt_symbol {
+ bt_word_t start;
+ bt_word_t end;
+ char *name;
+ struct bt_map *map;
+ struct bt_symbol *next;
+};
+
+/* backtrace user interface */
+void dump_backtrace(struct lt_config_shared *cfg, unsigned long flags);
+
+/* backtrace implementation helpers */
+int bt_dump(struct lt_config_shared *cfg, unsigned long flags);
+int bt_find_map(struct lt_config_shared *cfg, struct bt_map **map,
+ bt_word_t ip);
+int bt_find_symbol(struct lt_config_shared *cfg, struct bt_symbol **sym,
+ struct bt_map *map, bt_word_t ip);
+void bt_display(struct lt_config_shared *cfg, char *text);
+
+#endif /* BACKTRACE_H*/
diff --git a/src/config.h b/src/config.h
index b2dc73a..1ac519e 100644
--- a/src/config.h
+++ b/src/config.h
@@ -442,9 +442,13 @@ struct lt_error_sym* lt_error_sym_get(struct lt_config_shared *cfg,
int lt_error_sym_exit(struct lt_config_audit *cfg,
struct lt_symbol *sym,
struct timeval *tv,
+ struct link_map *lr,
+ const La_regs *inregs,
La_retval *outregs);
int lt_error_set_retval(struct lt_config_audit *cfg,
unsigned long ret, La_retval *outregs);
+void lt_error_get_retaddr(struct lt_config_audit *cfg,
+ void **ret_addr, const La_regs *inregs);
#define PRINT(fmt, args...) \
do { \
diff --git a/src/run.c b/src/run.c
index 2c2318c..34fccc6 100644
--- a/src/run.c
+++ b/src/run.c
@@ -405,8 +405,10 @@ static void run_cleanup(struct lt_config_app *cfg,
if (lt_sh(cfg, pipe))
lt_fifo_notify_cleanup(cfg);
- if (cfg->output_tty)
+ if (cfg->output_tty) {
tty_close(cfg);
+ close(pa->fd_tty_master);
+ }
remove_dir(cfg, pa->dir);
}
diff --git a/src/sysdeps/i686/error.c b/src/sysdeps/i686/error.c
index fd675a5..9aaa976 100644
--- a/src/sysdeps/i686/error.c
+++ b/src/sysdeps/i686/error.c
@@ -13,3 +13,10 @@ int lt_error_set_retval(struct lt_config_audit *cfg,
return 0;
}
+
+void lt_error_get_retaddr(struct lt_config_audit *cfg,
+ void **ret_addr, const La_regs *inregs)
+{
+ void **stack = (void**) inregs->lr_esp;
+ *ret_addr = *stack;
+}
diff --git a/src/sysdeps/x86_64/error.c b/src/sysdeps/x86_64/error.c
index 36784cd..bdad198 100644
--- a/src/sysdeps/x86_64/error.c
+++ b/src/sysdeps/x86_64/error.c
@@ -13,3 +13,10 @@ int lt_error_set_retval(struct lt_config_audit *cfg,
return 0;
}
+
+void lt_error_get_retaddr(struct lt_config_audit *cfg,
+ void **ret_addr, const La_regs *inregs)
+{
+ void **stack = (void**) inregs->lr_rsp;
+ *ret_addr = *stack;
+}