From d2a6151f8da8b8a2d20f76560e1b2d9ca670dbdc Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=B8ren=20Sandmann?= Date: Sat, 29 Mar 2008 16:00:33 +0000 Subject: [PATCH] Beginning of a dwarf unwinder. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Sat Mar 29 11:14:38 2008 Søren Sandmann * unwind.[ch], testunwind.c: Beginning of a dwarf unwinder. svn path=/trunk/; revision=405 --- ChangeLog | 4 + Makefile.am | 42 +++++-- binparser.h | 2 +- elfparser.c | 35 ++++-- elfparser.h | 1 + module/sysprof-module.c | 6 +- testunwind.c | 27 +++++ unwind.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++ unwind.h | 3 + 9 files changed, 407 insertions(+), 19 deletions(-) create mode 100644 testunwind.c create mode 100644 unwind.c create mode 100644 unwind.h diff --git a/ChangeLog b/ChangeLog index 2e511f4..fd08474 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Sat Mar 29 11:14:38 2008 Søren Sandmann + + * unwind.[ch], testunwind.c: Beginning of a dwarf unwinder. + Sat Mar 29 08:05:46 2008 Søren Sandmann * module/sysprof-module.c: x86 merge happened in 2.6.25 diff --git a/Makefile.am b/Makefile.am index fd7f750..a5a8180 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,12 +3,6 @@ DIST_SUBDIRS = module bin_PROGRAMS = sysprof-text -noinst_PROGRAMS = testelf -testelf_SOURCES = testelf.c demangle.c elfparser.c elfparser.h binparser.c binparser.h -testelf_CPPFLAGS = \ - $(CORE_DEP_CFLAGS) -testelf_LDADD = $(CORE_DEP_LIBS) - if BUILD_GUI bin_PROGRAMS += sysprof endif @@ -33,9 +27,12 @@ SYSPROF_CORE = \ sformat.c \ stackstash.h \ stackstash.c \ - module/sysprof-module.h \ + unwind.h \ + unwind.c \ watch.h \ - watch.c + watch.c \ + \ + module/sysprof-module.h # # GUI version @@ -92,3 +89,32 @@ EXTRA_DIST = \ insert-module: /sbin/modprobe -r sysprof-module /sbin/modprobe sysprof-module + +# +# Test programs +# +noinst_PROGRAMS = testelf testunwind + +# testunwind +testunwind_SOURCES = \ + testunwind.c \ + demangle.c \ + elfparser.c \ + elfparser.h \ + binparser.c \ + binparser.h \ + unwind.c \ + unwind.h +testunwind_CPPFLAGS = $(CORE_DEP_CFLAGS) +testunwind_LDADD = $(CORE_DEP_LIBS) + +# testelf +testelf_SOURCES = \ + testelf.c \ + demangle.c \ + elfparser.c \ + elfparser.h \ + binparser.c \ + binparser.h +testelf_CPPFLAGS = $(CORE_DEP_CFLAGS) +testelf_LDADD = $(CORE_DEP_LIBS) diff --git a/binparser.h b/binparser.h index ebedf40..de34be5 100644 --- a/binparser.h +++ b/binparser.h @@ -27,7 +27,7 @@ typedef struct BinField BinField; * manipulated with methods * * goto - go to absolute position from file start - * goto_rel - go to relative positio + * goto_rel - go to relative position * goto_record_rel - skip the given number of records * align - move forward until aligned to given width * save/restore - save/restore the current offset (stack) diff --git a/elfparser.c b/elfparser.c index e014438..5224618 100644 --- a/elfparser.c +++ b/elfparser.c @@ -470,16 +470,20 @@ read_table (ElfParser *parser, n_functions++; #if 0 - g_print (" symbol: %s: %lx\n", get_string_indirect (parser->parser, - parser->sym_format, "st_name", - str_table->offset), addr - parser->text_section->load_address); - g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n", addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); + g_print (" symbol: %s: %lx\n", + get_string_indirect (parser->parser, + parser->sym_format, "st_name", + str_table->offset), + addr - parser->text_section->load_address); + g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n", + addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); #endif } else if (addr != 0) { #if 0 - g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n", addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); + g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n", + addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); #endif } @@ -653,16 +657,29 @@ elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32) } const guchar * -elf_parser_get_eh_frame (ElfParser *parser) +get_section (ElfParser *parser, + const char *name) { - const Section *eh_frame = find_section (parser, ".eh_frame", SHT_PROGBITS); + const Section *section = find_section (parser, name, SHT_PROGBITS); - if (eh_frame) - return bin_parser_get_data (parser->parser) + eh_frame->offset; + if (section) + return bin_parser_get_data (parser->parser) + section->offset; else return NULL; } +const guchar * +elf_parser_get_eh_frame (ElfParser *parser) +{ + return get_section (parser, ".eh_frame"); +} + +const guchar * +elf_parser_get_debug_frame (ElfParser *parser) +{ + return get_section (parser, ".debug_frame"); +} + const char * elf_parser_get_sym_name (ElfParser *parser, const ElfSym *sym) diff --git a/elfparser.h b/elfparser.h index 41bc678..10ba62b 100644 --- a/elfparser.h +++ b/elfparser.h @@ -28,6 +28,7 @@ void elf_parser_free (ElfParser *parser); const char * elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32); const guchar *elf_parser_get_eh_frame (ElfParser *parser); +const guchar *elf_parser_get_debug_frame (ElfParser *parser); gulong elf_parser_get_text_offset (ElfParser *parser); diff --git a/module/sysprof-module.c b/module/sysprof-module.c index cb1c0f5..8052f37 100644 --- a/module/sysprof-module.c +++ b/module/sysprof-module.c @@ -223,7 +223,11 @@ trace_kernel (struct pt_regs *regs, bp = 0; #endif - dump_trace(NULL, regs, stack, bp, &backtrace_ops, &info); + dump_trace(NULL, regs, stack, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) + bp, +#endif + &backtrace_ops, &info); trace->n_kernel_words = info.pos; diff --git a/testunwind.c b/testunwind.c new file mode 100644 index 0000000..c6b4083 --- /dev/null +++ b/testunwind.c @@ -0,0 +1,27 @@ +#include "elfparser.h" +#include "unwind.h" + +int +main (int argc, char **argv) +{ + const guchar *data; + ElfParser *elf; + + if (argc == 1) + { + g_print ("no arg\n"); + return -1; + } + + elf = elf_parser_new (argv[1], NULL); + + if (!elf) + { + g_print ("NO ELF!!!!\n"); + return -1; + } + + unwind (elf); + + return 0; +} diff --git a/unwind.c b/unwind.c new file mode 100644 index 0000000..276b1f2 --- /dev/null +++ b/unwind.c @@ -0,0 +1,306 @@ +#include "elfparser.h" +#include "binparser.h" +#include + +/* FIXME: endianness, 64 bit */ + +static guint64 +get_length (const guchar **data) +{ + guint64 len; + + len = *(guint32 *)*data; + + *data += 4; + + if (len == 0xffffffff) + { + len = *(guint64 *)data; + *data += 8; + } + + return len; +} + +static guint64 +decode_uleb128 (const guchar **data) +{ + guint64 result; + int shift; + guchar b; + + result = 0; + shift = 0; + do + { + b = *(*data)++; + result |= (b & 0x7f) << shift; + shift += 7; + + } while (b & 0x80); + + return result; +} + +static gint64 +decode_sleb128 (const guchar **data) +{ + gint64 result; + int shift; + guchar b; + + result = 0; + shift = 0; + do + { + b = *(*data)++; + result |= (b & 0x7f) << shift; + shift += 7; + } while (b & 0x80); + + if (b & 0x40 && shift < 64) + result |= - (1 << shift); + + return result; +} + +static void +decode_block (const guchar **data) +{ + int len; + + /* FIXME */ + + len = decode_uleb128 (data); + + (*data) += len; +} + +static gulong +decode_address (const guchar **data) +{ + /* FIXME */ + gulong r; + + r = *(guint32 *)*data; + (*data) += 4; + return r; +} + +static const char * +decode_instruction (const guchar **data) +{ + int opcode = *(*data)++; + int high2 = (opcode & 0xc0) >> 6; + int low6 = (opcode & 0x3f); + + if (high2 == 0x01) + { + return "DW_CFA_advance_loc"; + } + else if (high2 == 0x02) + { + g_print ("register: %d\n", low6); + g_print ("offset: %llu\n", decode_uleb128 (data)); + + return "DW_CFA_offset"; + } + else if (high2 == 0x03) + { + return "DW_CFA_restore"; + } + else + { + g_assert ((opcode & 0xc0) == 0); + + switch (opcode) + { + case 0x0: + return "DW_CFA_nop"; + + case 0x01: + g_print ("addr: %p\n", (void *)decode_address (data)); + return "DW_CFA_set_loc"; + + case 0x02: + (*data)++; + return "DW_CFA_advance_loc1"; + + case 0x03: + (*data) += 2; + return "DW_CFA_advance_loc2"; + + case 0x04: + (*data) += 4; + return "DW_CFA_advance_loc4"; + + case 0x05: + decode_uleb128 (data); + decode_uleb128 (data); + return "DW_CFA_offset_extended"; + + case 0x06: + decode_uleb128 (data); + return "DW_CFA_restore_extended"; + + case 0x07: + decode_uleb128 (data); + return "DW_CFA_undefined"; + + case 0x08: + decode_uleb128 (data); + return "DW_CFA_same_value"; + + case 0x09: + decode_uleb128 (data); + decode_uleb128 (data); + return "DW_CFA_register"; + + case 0x0a: + return "DW_CFA_remember_state"; + + case 0x0b: + return "DW_CFA_restore_state"; + + case 0x0c: + g_print ("reg: %llu\n", decode_uleb128 (data)); + g_print ("off: %llu\n", decode_uleb128 (data)); + return "DW_CFA_def_cfa"; + + case 0x0d: + decode_uleb128 (data); + return "DW_CFA_def_cfa_register"; + + case 0x0e: + decode_uleb128 (data); + return "DW_CFA_def_cfa_offset"; + + case 0x0f: + decode_block (data); + return "DW_CFA_def_cfa_expression"; + + case 0x10: + decode_uleb128 (data); + decode_block (data); + return "DW_CFA_expression"; + + case 0x11: + decode_uleb128 (data); + decode_sleb128 (data); + return "DW_CFA_offset_extended_sf"; + + case 0x12: + decode_uleb128 (data); + decode_sleb128 (data); + return "DW_CFA_def_cfa_sf"; + + case 0x13: + decode_sleb128 (data); + return "DW_CFA_def_cfa_offset_sf"; + + case 0x14: + decode_uleb128 (data); + decode_uleb128 (data); + return "DW_CFA_val_offset"; + + case 0x15: + decode_uleb128 (data); + decode_sleb128 (data); + return "DW_CFA_val_offset_sf"; + + case 0x16: + decode_uleb128 (data); + decode_block (data); + return "DW_CFA_val_expression"; + + case 0x1c: + return "DW_CFA_lo_user"; + + case 0x3f: + return "DW_CFA_hi_user"; + + default: + return "UNKNOWN INSTRUCTION"; + } + } +} + + +static void +decode_entry (const guchar *data) +{ + guint64 len, aug_len; + const guchar *end; + gboolean has_augmentation; + + len = get_length (&data); + + end = data + len; + + g_print ("length: %llu\n", len); + + /* CIE_id is 0 for eh frames, and 0xffffffff/0xffffffffffffffff for .debug_frame */ + + g_print ("id: %d\n", *(guint32 *)data); + + data += 4; + + g_print ("version: %d\n", *data); + + data += 1; + + g_print ("augmentation: %s\n", data); + + has_augmentation = strchr (data, 'z'); + + data += strlen (data) + 1; + + g_print ("code alignment: %llu\n", decode_uleb128 (&data)); + + g_print ("data alignment: %lld\n", decode_sleb128 (&data)); + + g_print ("return register: %llu\n", decode_uleb128 (&data)); + + if (has_augmentation) + { + g_print ("augmentation length: %llu\n", (aug_len = decode_uleb128 (&data))); + + data += aug_len; + } + + while (data < end) + g_print (" %s\n", decode_instruction (&data)); +} + +/* The correct API is probably something like + * + * gboolean + * unwind (ElfParser *parser, + * gulong *regs + * int n_regs, + * MemoryReader reader, + * gpointer data); + * + */ +void +unwind (ElfParser *elf) +{ + const guchar *data; + + if ((data = elf_parser_get_debug_frame (elf))) + { + g_print ("Using .debug_frame\n"); + } + else if ((data = elf_parser_get_eh_frame (elf))) + { + g_print ("Using .eh_frame\n"); + } + else + { + g_print ("no debug info found\n"); + return; + } + + decode_entry (data); +} + diff --git a/unwind.h b/unwind.h new file mode 100644 index 0000000..2827e74 --- /dev/null +++ b/unwind.h @@ -0,0 +1,3 @@ +#include + +void unwind (const guchar *data); -- 2.7.4