>From e6c0a0109a7f71196d7a8fa5895a835912c092ff Mon Sep 17 00:00:00 2001 From: root <root@xxxxxxxxxxxxxxxx> Date: Mon, 4 May 2009 23:13:01 +0530 Subject: [PATCH] Provide stack unwinding feature This provides interface to stack unwinding (up & down) which in turn used by the local command. It uses elf and dwarf library interfaces. Signed-off-by: Sharyathi Nagesh <sharyath@xxxxxxxxxxxxxxxxxx> Signed-off-by: M. Mohan Kumar <mohan@xxxxxxxxxx> --- extensions/local.c | 70 +++++- extensions/local.h | 3 + extensions/local.mk | 7 +- extensions/unwind_dw.c | 694 ++++++++++++++++++++++++++++++++++++++++++++++++ extensions/unwind_dw.h | 159 +++++++++++ 5 files changed, 927 insertions(+), 6 deletions(-) create mode 100644 extensions/unwind_dw.c create mode 100644 extensions/unwind_dw.h diff --git a/extensions/local.c b/extensions/local.c index e6559cd..dc6235c 100644 --- a/extensions/local.c +++ b/extensions/local.c @@ -30,6 +30,7 @@ #include "defs.h" /* From the crash source top-level directory */ #include "../netdump.h" #include "local.h" +#include "unwind_dw.h" /* Main Function */ void cmd_local(); @@ -115,6 +116,8 @@ initialize_local() "Support for only KDUMP/NETDUMPs is available"); return 0; } + + unwind_dw_init(fd, elf_descriptor, local->pc); return 1; } @@ -166,13 +169,71 @@ static int initialize_register() return 1; } +void *save_register(void) +{ + void *regs; + + switch(get_netdump_arch()) + { + case EM_386: + regs = calloc(1, sizeof(struct pt_regs_x86)); + if(!regs){ + fprintf(fp, "\n Memory allocation failed"); + return NULL; + } + memcpy(regs, local->regs, sizeof(struct pt_regs_x86)); + break; + case EM_PPC64: + regs = calloc(1, sizeof(struct pt_regs_ppc64)); + if(!regs){ + fprintf(fp, "\n Memory allocation failed"); + return NULL; + } + memcpy(regs, local->regs, sizeof(struct pt_regs_ppc64)); + break; + case EM_X86_64: + regs = calloc(1, sizeof(struct pt_regs_x86_64)); + if(!regs){ + fprintf(fp, "\n Memory allocation failed"); + return NULL; + } + memcpy(regs, local->regs, sizeof(struct pt_regs_x86_64)); + break; + default: + error(FATAL, + "Support for ELF machine type %d not available", get_netdump_arch()); + return NULL; + } + return regs; +} + +int restore_register(void *regs) +{ + switch(get_netdump_arch()) + { + case EM_386: + memcpy(local->regs, regs, sizeof(struct pt_regs_x86)); + break; + case EM_PPC64: + memcpy(local->regs, regs, sizeof(struct pt_regs_ppc64)); + break; + case EM_X86_64: + memcpy(local->regs, regs, sizeof(struct pt_regs_x86_64)); + break; + default: + error(FATAL, + "Support for ELF machine type %d not available", get_netdump_arch()); + break; + } + return 0; +} + int static initial_setup(void) { Elf_Cmd elf_cmd = ELF_C_READ; Dwarf_Cmd dwarf_cmd = DWARF_C_READ; - int fd; if (elf_version(EV_CURRENT) == EV_NONE) { error(FATAL, @@ -228,6 +289,7 @@ _fini() } elf_end(elf_descriptor); close(fd); + unwind_dw_fini(); return 1; } @@ -258,11 +320,11 @@ cmd_local() { case STACK_UNWIND_UP: local->flags = STACK_UNWIND_UP; - /* Not implemented */ + unwind_dw_up(local->pc); break; case STACK_UNWIND_DOWN: local->flags = STACK_UNWIND_DOWN; - /* Not implemented */ + unwind_dw_down(local->pc); break; case DISPLAY_LOCALS: fprintf(fp, "display locals"); @@ -724,7 +786,9 @@ get_location(Dwarf_Attribute *location, Dwarf_Addr addr, size_t *len) char *help_local[] = { "local", /* command name */ "displays local variables in current context", /* short description */ + "params locals up down ...", /* arguments for the command */ " This command displays local variables as well as parameters.", + " This command allows unwind the stack .", "\nEXAMPLE", " crash> local <locals|params>", NULL diff --git a/extensions/local.h b/extensions/local.h index 320592b..d4d6df4 100644 --- a/extensions/local.h +++ b/extensions/local.h @@ -86,3 +86,6 @@ struct pt_regs_x86_64 { unsigned long sp; unsigned long ss; }; + +extern void *save_register(void); +extern int restore_register(void *regs); diff --git a/extensions/local.mk b/extensions/local.mk index 3ba385a..2aab2dc 100644 --- a/extensions/local.mk +++ b/extensions/local.mk @@ -7,8 +7,9 @@ endif all: local.so -local.so: ../defs.h local.c local.h - gcc -nostartfiles -shared -rdynamic -o $@ local.c -fPIC -ldw -L ../../elfutils-0.137/libdw -I ../../elfutils-0.137/libdw -I ../../elfutils-0.137/libelf/ -D$(TARGET) $(TARGET_CFLAGS) $(ADD_CFLAGS) -Wall; +local.so: ../defs.h local.c local.h unwind_dw.c unwind_dw.h + gcc -nostartfiles -shared -g -rdynamic -o $@ local.c unwind_dw.c -fPIC -ldw -L ../../elfutils-0.137/libdw -I ../../elfutils-0.137/libdw -I ../../elfutils-0.137/libelf/ -D$(TARGET) $(TARGET_CFLAGS) $(ADD_CFLAGS) -Wall; + clean: - rm -rf local.o local.so + rm -rf local.o local.so unwind_dw.o unwind_dw.so diff --git a/extensions/unwind_dw.c b/extensions/unwind_dw.c new file mode 100644 index 0000000..930e785 --- /dev/null +++ b/extensions/unwind_dw.c @@ -0,0 +1,694 @@ +/* unwind_dw.c - Dwarf stack unwind interface + * + * Copyright (c) International Business Machines Corp., 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdint.h> +#include <byteswap.h> +#include <limits.h> +#include <string.h> +#include <fcntl.h> +#include <libelf.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <elfutils/libdw.h> +#include <dwarf.h> +#include "unwind_dw.h" +#include "defs.h" +#include "local.h" + +uint64_t cf; +int64_t df; +struct cie_info *cielist; +struct fde_info *fdelist; +char *elf_buff; +struct unwind_info *ui; + +extern struct local_context *local; + +uint8_t get1b(void *readp) +{ + union unaligned *up = readp; + return (char)up->u2; +} + +uint32_t get4b(void *readp) +{ + union unaligned *up = readp; + return up->u4; +} + +uint32_t get4b_s(void *readp) +{ + union unaligned *up = readp; + return up->s4; +} + +uint64_t get8b(void *readp) +{ + union unaligned *up = readp; + return up->u8; +} + +uint64_t get8b_s(void *readp) +{ + union unaligned *up = readp; + return up->s8; +} + + + +int process_opcode(unsigned char **i_inst, struct cfa_info *infop) +{ + unsigned char *inst = *i_inst; + int pr_op, ex_op; + uint64_t oper1, oper2; + int regi; + uint16_t *temp16p; + uint64_t *temp64p; + uint64_t value; + + pr_op = *inst & 0xc0; /* primary opcode is in top 2 bits of instruction */ + ex_op = *inst; + + switch (pr_op) { + case DW_CFA_advance_loc: + infop->pc_reg += (*inst & 0x3f) * cf; + inst++; + break; + case DW_CFA_offset: + oper1 = *inst & 0x3f; + inst++; + get_uleb128(oper2, inst); + oper2 *= df; + infop->oper1 = oper1; + if (readmem(infop->cfa_address + oper2, KVADDR, &value, sizeof(value), "DW_CFA_offset", RETURN_ON_ERROR) == FALSE) + fprintf(fp, "readmem error\n"); + + infop->oper2 = value; + break; + case DW_CFA_restore: + regi = *inst & 0x3f; + infop->oper1 = regi; + inst++; /* **** */ + break; + default: + break; + } + + if (pr_op) + goto label; + + switch (ex_op) { + case DW_CFA_set_loc: + inst++; + get_uleb128(oper1, inst); + infop->pc_reg = oper1; + break; + case DW_CFA_advance_loc1: + inst++; + infop->pc_reg += *inst++ * cf; + break; + case DW_CFA_advance_loc2: + inst++; + temp16p = (uint16_t *)inst; + infop->pc_reg += *temp16p * cf; + inst+=2; + break; + case DW_CFA_advance_loc4: + inst++; + temp64p = (uint64_t *)inst; + infop->pc_reg += *temp64p * cf; + inst+=4; + break; + case DW_CFA_offset_extended: + inst++; + get_uleb128(oper1, inst); + get_uleb128(oper2, inst); + infop->oper1 = oper1; + oper2 *= df; + if (readmem(infop->cfa_address + oper2, KVADDR, &value, sizeof(value), "DW_CFA_offset_extended", RETURN_ON_ERROR) == FALSE) + fprintf(fp, "readmem error\n"); + break; + case DW_CFA_restore_extended: + inst++; + get_uleb128(oper1, inst); + infop->oper1 = oper1; /* ***** */ + break; + case DW_CFA_undefined: + inst++; + get_uleb128(oper1, inst); + infop->oper1 = oper1; + break; + case DW_CFA_same_value: + inst++; + get_uleb128(oper1, inst); + infop->oper1 = oper1; + break; + case DW_CFA_register: + inst++; + get_uleb128(oper1, inst); + get_uleb128(oper2, inst); + infop->oper1 = oper1; + infop->oper2 = local->fetch_register(oper2); + break; + case DW_CFA_remember_state: + inst++; + break; + case DW_CFA_restore_state: + inst++; + break; + case DW_CFA_def_cfa: + inst++; + get_uleb128(oper1, inst); + get_uleb128(oper2, inst); + infop->cfa_reg = oper1; + infop->cfa_offset = oper2; + infop->cfa_address = local->fetch_register(oper1) + oper2; + break; + case DW_CFA_def_cfa_register: + inst++; + get_uleb128(oper1, inst); + infop->cfa_reg = oper1; + infop->cfa_address = local->fetch_register(infop->cfa_reg) + infop->cfa_offset; + break; + case DW_CFA_def_cfa_offset: + inst++; + get_uleb128(oper1, inst); + infop->cfa_offset = oper1; + infop->cfa_address = local->fetch_register(infop->cfa_reg) + oper1; + break; + case DW_CFA_def_cfa_expression: + inst++; + get_uleb128(oper1, inst); + inst += oper1; + break; + case DW_CFA_expression: + inst++; + get_uleb128(oper1, inst); + get_uleb128(oper2, inst); + inst += oper2; + break; + case DW_CFA_offset_extended_sf: + inst++; + get_uleb128(oper1, inst); + get_sleb128(oper2, inst); + infop->oper1 = oper1; + oper2 *= df; + if (readmem(infop->cfa_address + oper2, KVADDR, &value, sizeof(value), "DW_CFA_offset_extended_sf", RETURN_ON_ERROR) == FALSE) + fprintf(fp, "reamem error\n"); + infop->oper2 = value; + break; + case DW_CFA_def_cfa_sf: + inst++; + get_uleb128(oper1, inst); + get_sleb128(oper2, inst); + infop->cfa_reg = oper1; + infop->cfa_offset = oper2 * df; + infop->cfa_address = local->fetch_register(infop->cfa_reg) + infop->cfa_offset; + break; + case DW_CFA_def_cfa_offset_sf: + inst++; + get_sleb128(oper1, inst); + infop->cfa_offset = oper1 * df; + infop->cfa_address = local->fetch_register(infop->cfa_reg) + infop->cfa_offset; + break; + case DW_CFA_val_offset: + inst++; + get_uleb128(oper1, inst); + get_sleb128(oper2, inst); + infop->oper1 = oper1; + oper2 *= df; + if (readmem(infop->cfa_address + oper2, KVADDR, &value, sizeof(value), "DW_CFA_val_offset", RETURN_ON_ERROR) == FALSE) + fprintf(fp, "reamem says error\n"); + infop->oper2 = value; + break; + case DW_CFA_val_offset_sf: + inst++; + get_uleb128(oper1, inst); + get_sleb128(oper2, inst); + infop->oper1 = oper1; + oper2 *= df; + if (readmem(infop->cfa_address + oper2, KVADDR, &value, sizeof(value), "DW_CFA_val_offset_sf", RETURN_ON_ERROR) == FALSE) + fprintf(fp, "reamem says error\n"); + infop->oper2 = value; + break; + case DW_CFA_val_expression: + inst++; + get_uleb128(oper1, inst); + get_uleb128(oper2, inst); + inst += oper2; + break; + case DW_CFA_low_user: + case DW_CFA_high_user: + inst++; + break; + case DW_CFA_nop: + inst++; + break; + default: + fprintf(fp, "unknown op: 0x%x\n", *inst); + inst++; + break; + } +label: + *i_inst = inst; + return 0; +} + +char *process_64(Elf *elfp, int fd, int *size) +{ + int index; + size_t str_idx; + Elf64_Shdr *symbol_shdr, *shdr; + Elf_Scn *secn; + size_t nr_sect; + char *strtab; + size_t offset; + char *buff; + + elf_getshstrndx(elfp, &str_idx); + elf_getshnum(elfp, &nr_sect); + + secn = elf_getscn(elfp, str_idx); + if (secn == NULL) + return NULL; + + symbol_shdr = elf64_getshdr(secn); + if (symbol_shdr->sh_type == SHT_STRTAB) { + strtab = malloc(symbol_shdr->sh_size); + + offset = lseek(fd, 0, SEEK_CUR); + if (lseek(fd, symbol_shdr->sh_offset, SEEK_SET) < 0) { + fprintf(fp, "lseek error\n"); + return NULL; + } + + if (read(fd, strtab, symbol_shdr->sh_size) != symbol_shdr->sh_size) { + fprintf(fp, "read error\n"); + return NULL; + } + } + + index = 0; + + do { + if (index == str_idx) + continue; + + secn = elf_getscn(elfp, index); + if (secn == NULL) + break; + + shdr = elf64_getshdr(secn); + + if (strcmp(strtab + shdr->sh_name, DEBUG_FRAME)) + continue; + buff = malloc(shdr->sh_size); + if (!buff) { + fprintf(fp, "can't allocate memory for buff\n"); + return NULL; + } + + if (lseek(fd, shdr->sh_offset, SEEK_SET) < 0) { + fprintf(fp, "lseek"); + return NULL; + } + if (read(fd, buff, shdr->sh_size) != shdr->sh_size) { + fprintf(fp, "read error\n"); + return NULL; + } + *size = shdr->sh_size; + break; + + index++; + } while (index++ < nr_sect); + free(strtab); + return buff; +} + +uint64_t unwind_dw(uint64_t address, int modify) +{ + uint8_t *inst, *endp; + int pr_op, ex_op; + char found = 0; + struct cfa_info info; + uint64_t location, return_address = 0; + int return_register; + struct cie_info *ciep; + struct fde_info *fdep; + + for (ciep = cielist; ciep && !found; ciep = ciep->next) { + for (fdep = ciep->fdelist; fdep && !found; fdep = fdep->next) { + if (address >= fdep->initial_location && address <= fdep->initial_location + fdep->address_range) { + inst = ciep->initial_instructions; + endp = inst + ciep->initial_instructions_length; + + return_register = ciep->return_address_register; + + location = fdep->initial_location; + info.pc_reg = location; + + while (inst < endp) { + pr_op = *inst & 0xc0; + ex_op = *inst; + info.oper1 = ULONG_MAX; + process_opcode(&inst, &info); + + if (info.oper1 == return_register) + return_address = info.oper2; + else if (info.oper1 != ULONG_MAX && modify) + local->assign_register(info.oper1, info.oper2); + } + + inst = fdep->instructions; + endp = inst + fdep->instructions_length; + + while (inst < endp) { + pr_op = *inst & 0xc0; + ex_op = *inst; + + if (ex_op == DW_CFA_set_loc || pr_op == DW_CFA_advance_loc) { + location += *inst & 0x3f; + if (address < location) { + found = 1; + break; + } + } + + info.oper1 = ULONG_MAX; + process_opcode(&inst, &info); + + if (info.oper1 == return_register) + return_address = info.oper2; + else if (info.oper1 != ULONG_MAX && modify) + local->assign_register(info.oper1, info.oper2); + } + + if (!found) { /* End of instruction seqeuence */ + if (address < fdep->initial_location + fdep->address_range) + found = 1; + if (modify) + local->assign_register(SP, info.cfa_address); + } + } + } + } + + return return_address; +} + +int unwind_dw_init(int fd, Elf *elfp, ulong pc) +{ + char *buff = NULL; + char *readp, *start; + uint64_t length, cie_id; + char *i_inst, *endp; + uint64_t location, range; + char *inst; + int sh_size; + uint64_t size; + struct cie_info *ciep; + struct fde_info *fdep; + struct unwind_info *up; + uint64_t addr; + uint64_t curr = pc; + void *regs; + + + ciep = NULL; + fdep = NULL; + + if (elf64_getehdr(elfp)) + elf_buff = buff = process_64(elfp, fd, &sh_size); + else { + fprintf(fp, "ELF32 support not yet implemented\n"); + return 0; + } + + if (!buff) { + fprintf(fp, "process_64 error\n"); + return 0; + } + + readp = buff; + endp = readp + sh_size; + + while (readp < endp) { + length = get4b(readp); + readp += 4; + + size = 4; + if (length == 0xffffffff) { + length = get8b(readp); + + start = readp + 8; + size = 8; + } + + start = readp; + + if (size == 4) { + cie_id = get4b(readp); + readp += 4; + } else { + cie_id = get8b(readp); + readp += 8; + } + + if (cie_id == 0xffffffff) { /* CIE Section */ + char *augm; + + if (!ciep) { + cielist = malloc(sizeof(struct cie_info)); + if (!cielist) { + fprintf(fp, "Unable to allocate memory for cielist\n"); + return 0; + } + ciep = cielist; + } else { + ciep->next = malloc(sizeof(struct cie_info)); + if (!ciep->next) { + fprintf(fp, "Unable to allocate memory for cielist\n"); + return 0; + } + ciep = ciep->next; + } + ciep->next = NULL; + ciep->fdelist = NULL; + fdep = NULL; + ciep->version = get1b(readp); + readp++; + ciep->augmentation = augm = readp; + readp += strlen(augm) + 1; + if (!strcmp(augm, "eh")) + readp += size; + + get_uleb128(cf, readp); + get_sleb128(df, readp); + ciep->code_alignment_factor = cf; + ciep->data_alignment_factor = df; + + ciep->return_address_register = *readp++; + + i_inst = readp; + ciep->initial_instructions = (uint8_t *)readp; + + readp = start + length; + ciep->initial_instructions_length = readp - i_inst; + } else { /* FDE Section */ + location = get8b(readp); + readp += 8; + range = get8b(readp); + readp += 8; + + inst = readp; + readp = start + length; + + if (!fdep) { + fdelist = malloc(sizeof(struct fde_info)); + if (!fdelist) { + fprintf(fp, "Unable to allocate memory for fdelist\n"); + return 0; + } + fdep = fdelist; + } else { + fdep->next = malloc(sizeof(struct fde_info)); + if (!fdep->next) { + fprintf(fp, "Unable to allocate memory for fdelist\n"); + return 0; + } + fdep = fdep->next; + } + + if (ciep && !ciep->fdelist) + ciep->fdelist = fdep; + fdep->next = NULL; + fdep->initial_location = location; + fdep->address_range = range; + fdep->instructions = (uint8_t *)inst; + fdep->instructions_length = readp - inst; + } + } + + + /* Save complete backtrace & register info */ + regs = save_register(); + ui = up = NULL; + addr = pc; + + ui = malloc(sizeof(struct unwind_info)); + up = ui; + + if (up == NULL) { + fprintf(fp, "Unable to allocate memory for unwind info " + " unwinding functionality is disabled\n"); + return 0; + } + + up->pc = pc; + up->regs = save_register(); + + addr = unwind_dw(pc, 1); + up->ret_addr = addr; + pc = addr; + + do { + pc = addr; + up->next = malloc(sizeof(struct unwind_info)); + if (up->next == NULL) { + fprintf(fp, "Unable to allocate memory for unwind info, down functionality disabled\n"); + return 0; + } + + up = up->next; + up->pc = pc; + up->regs = save_register(); + addr = unwind_dw(pc, 1); + up->ret_addr = addr; + + up->next = NULL; + + } while (addr); + + fprintf(fp, "\n\n"); + + restore_register(ui->regs); + local->pc = curr; + + struct cie_info *cnext; + struct fde_info *fnext; + + for (ciep = cielist; ciep; ) { + for (fdep = ciep->fdelist; fdep; ) { + fnext = fdep->next; + free(fdep); + fdep = fnext; + } + + cnext = ciep->next; + free(ciep); + ciep = cnext; + } + + cielist = ciep = NULL; + fdelist = fdep = NULL; + + + return 1; +} + +void unwind_dw_up(uint64_t pc) +{ + struct unwind_info *up; + + up = ui; + + while (up) { + if (up->pc == pc) { + pc = up->ret_addr; + break; + } + up = up->next; + } + + up = ui; + while (up) { + if (up->pc == pc) { + break; + } + up = up->next; + } + + if (!up) { + fprintf(fp, "Already in top of frame\n"); + return; + } + + restore_register(up->regs); + + local->pc = pc; + fprintf(fp, "Switching to stack frame 0x%lx\n", local->fetch_register(SP)); +} + +void unwind_dw_down(uint64_t pc) +{ + uint64_t ret_addr; + struct unwind_info *up; + + up = ui; + + do { + if (up->ret_addr == pc) + break; + } while ((up = up->next)); + + if (!up) { + fprintf(fp, "Already in the bottom frame\n"); + return; + } + + restore_register(up->regs); + ret_addr = up->pc; + + if (ret_addr) { + local->pc = ret_addr; + fprintf(fp, "Switching to stack frame 0x%lx\n", local->fetch_register(SP)); + } +} + +void unwind_dw_fini(void) +{ + struct cie_info *ciep, *cnext; + struct fde_info *fdep, *fnext; + + free(elf_buff); + + for (ciep = cielist; ciep; ) { + for (fdep = ciep->fdelist; fdep; ) { + fnext = fdep->next; + free(fdep); + fdep = fnext; + } + + cnext = ciep->next; + free(ciep); + ciep = cnext; + } +} diff --git a/extensions/unwind_dw.h b/extensions/unwind_dw.h new file mode 100644 index 0000000..8812daf --- /dev/null +++ b/extensions/unwind_dw.h @@ -0,0 +1,159 @@ +/* unwind_dw.h - Dwarf stack unwind interface + * + * Copyright (c) International Business Machines Corp., 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef PPC64 +#define SP 1 +#else +#define SP 7 +#endif + +#define DEBUG_FRAME ".debug_frame" + +/* unwind_dw functions */ +int unwind_dw_init(int fd, Elf *elfp, ulong pc); +void unwind_dw_up(uint64_t pc); +void unwind_dw_down(uint64_t pc); +void unwind_dw_fini(void); + +struct cie_info { + uint64_t length; + char *augmentation; + char version; + uint64_t code_alignment_factor; + int64_t data_alignment_factor; + uint8_t *initial_instructions; + uint64_t initial_instructions_length; + uint8_t return_address_register; + uint64_t offset; + int64_t index; + + struct cie_info *next; + struct fde_info *fdelist; +}; + +struct fde_info { + uint64_t initial_location; + uint64_t address_range; + uint8_t *instructions; + uint64_t instructions_length; + uint64_t offset; + uint8_t *fde_bytes; + uint64_t fde_byte_length; + + struct fde_info *next; +}; + +struct cfa_info { + uint64_t oper1; + uint64_t oper2; + uint64_t pc_reg; + char cfa_reg; + uint64_t cfa_offset; + uint64_t cfa_address; +}; + +struct unwind_info { + ulong pc; + ulong ret_addr; + void *regs; + struct unwind_info *next; +}; + +/* Number decoding macros. See 7.6 Variable Length Data. */ +#define get_uleb128(var, addr) \ +do { \ + uint8_t __b = *addr++; \ + var = __b & 0x7f; \ + if (__b & 0x80) { \ + __b = *addr++; \ + var |= (__b & 0x7f) << 7; \ + if (__b & 0x80) { \ + __b = *addr++; \ + var |= (__b & 0x7f) << 14; \ + if (__b & 0x80) { \ + __b = *addr++; \ + var |= (__b & 0x7f) << 21; \ + if (__b & 0x80) \ + /* Other implementation set VALUE to UINT_MAX in this \ + case. So we better do this as well. */ \ + var = UINT_MAX; \ + } \ + } \ + } \ +} while (0) + +/* The signed case is a big more complicated. */ +#define get_sleb128(var, addr) \ + do { \ + uint8_t __b = *addr++; \ + int32_t __res = __b & 0x7f; \ + if ((__b & 0x80) == 0) \ + { \ + if (__b & 0x40) \ + __res |= 0xffffff80; \ + } \ + else \ + { \ + __b = *addr++; \ + __res |= (__b & 0x7f) << 7; \ + if ((__b & 0x80) == 0) \ + { \ + if (__b & 0x40) \ + __res |= 0xffffc000; \ + } \ + else \ + { \ + __b = *addr++; \ + __res |= (__b & 0x7f) << 14; \ + if ((__b & 0x80) == 0) \ + { \ + if (__b & 0x40) \ + __res |= 0xffe00000; \ + } \ + else \ + { \ + __b = *addr++; \ + __res |= (__b & 0x7f) << 21; \ + if ((__b & 0x80) == 0) \ + { \ + if (__b & 0x40) \ + __res |= 0xf0000000; \ + } \ + else \ + /* Other implementation set VALUE to INT_MAX in this \ + case. So we better do this as well. */ \ + __res = INT_MAX; \ + } \ + } \ + } \ + var = __res; \ + } while (0) + +union unaligned +{ + void *readp; + uint16_t u2; + uint32_t u4; + uint64_t u8; + int16_t s2; + int32_t s4; + int64_t s8; +} __attribute__ ((packed)); + + -- 1.6.0.2
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility