diff options
Diffstat (limited to 'unwind.c')
-rw-r--r-- | unwind.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/unwind.c b/unwind.c new file mode 100644 index 0000000..d7c23e1 --- /dev/null +++ b/unwind.c @@ -0,0 +1,399 @@ +#include "elfparser.h" +#include <string.h> +#include "util.h" + +/* Pointer encodings, from dwarf2.h. */ +typedef enum +{ + DW_EH_PE_absptr = 0x00, /* */ + DW_EH_PE_omit = 0xff, /* Value is not there */ + + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_signed = 0x08, + + DW_EH_PE_pcrel = 0x10, /* Value is *(cur + val) */ + DW_EH_PE_textrel = 0x20, /* Value is *(&text + val) */ + DW_EH_PE_datarel = 0x30, /* Value is *(&data + val) */ + DW_EH_PE_funcrel = 0x40, /* Value is *(fde.pc_begin + val) */ + DW_EH_PE_aligned = 0x50, /* Value is absolute, and stored + * at next align */ + + DW_EH_PE_indirect = 0x80 +} PointerEncoding; + +typedef struct EncodedPointer EncodedPointer; +struct EncodedPointer +{ + PointerEncoding encoding; + guint64 value; +}; + +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: "FMT64"\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: "FMT64"\n", decode_uleb128 (data)); + g_print ("off: "FMT64"\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"; + } + } +} + +typedef struct CIE CIE; +struct CIE +{ + PointerEncoding encoding; +}; + +static void +decode_cie (const guchar **data, const guchar *end) +{ + gboolean has_augmentation; + guint64 aug_len; + const char *augmentation; + CIE *cie; + int i, field; + + g_print ("version: %d\n", *(*data)++); + + augmentation = (*data); + + *data += strlen (*data) + 1; + + g_print ("code alignment: "FMT64"\n", decode_uleb128 (data)); + + g_print ("data alignment: %lld\n", decode_sleb128 (data)); + + g_print ("return register: "FMT64"\n", decode_uleb128 (data)); + + g_print ("augmentation: %s\n", augmentation); + + if (augmentation[0] == 'z') + { + aug_len = decode_uleb128 (data); + + g_print ("len: "FMT64"\n", aug_len); + + for (i = 1; augmentation[i] != 0; ++i) + { + if (augmentation[i] == 'L') + { + + } + } + } + + + if (has_augmentation) + { + g_print ("%x\n", **data); + + *data += aug_len; + } + + while (*data < end) + g_print (" %s\n", decode_instruction (data)); +} + +static gboolean +decode_fde (const guchar **data, const guchar *end) +{ + + + return FALSE; +} + +static gboolean +decode_entry (const guchar **data, gboolean eh_frame) +{ + guint64 len; + const guchar *end; + gboolean is_cie; + guint64 id; + + len = get_length (data); + + if (len == 0) + return FALSE; + + end = *data + len; + + g_print ("length: "FMT64"\n", len); + + /* CIE_id is 0 for eh frames, and 0xffffffff/0xffffffffffffffff for .debug_frame */ + + id = *(guint32 *)*data; + + g_print ("id: %lld\n", id); + + is_cie = (eh_frame && id == 0) || (!eh_frame && id == 0xffffffff); + + if (is_cie) + g_print ("is cie\n"); + else + g_print ("is not cie\n"); + + *data += 4; + + if (is_cie) + decode_cie (data, end); + else + decode_fde (data, end); + + return TRUE; +} + +/* 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; + gboolean eh_f; + + if ((data = elf_parser_get_debug_frame (elf))) + { + g_print ("Using .debug_frame\n"); + eh_f = FALSE; + } + else if ((data = elf_parser_get_eh_frame (elf))) + { + g_print ("Using .eh_frame\n"); + eh_f = TRUE; + } + else + { + g_print ("no debug info found\n"); + return; + } + + while (decode_entry (&data, eh_f)) + return ; + ; +} + |