Index: crash-4.0-8.9-build/extensions/local.c =================================================================== --- crash-4.0-8.9-build.orig/extensions/local.c 2009-05-24 18:48:12.000000000 +0530 +++ crash-4.0-8.9-build/extensions/local.c 2009-05-24 18:48:58.000000000 +0530 @@ -29,6 +29,7 @@ #include <elfutils/libdw.h> #include "defs.h" /* From the crash source top-level directory */ #include "local/local.h" +#include "local/unwind_dw.h" /* Main Function */ void cmd_local(); @@ -67,6 +68,7 @@ static int str_to_option(char *str); static void read_vmcore(ulong , size_t, void *); + static Elf *elf_descriptor; static Dwarf *dbg; static int fd; @@ -118,6 +120,10 @@ "Support for only KDUMP/NETDUMPs is available"); return 0; } + + /* Initialize stack frame info */ + unwind_dw_info_init(local->pc); + return 1; } @@ -171,6 +177,65 @@ } 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) @@ -198,6 +263,10 @@ dwarf_errmsg(dwarf_errno())); return 0; } + + if (!unwind_dw_init(fd, elf_descriptor)) + return 0; + return 1; } @@ -233,6 +302,7 @@ } elf_end(elf_descriptor); close(fd); + unwind_dw_fini(); return 1; } @@ -263,19 +333,19 @@ { 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"); + fprintf(fp, "display locals for function %s", closest_symbol(local->pc)); local->flags = DISPLAY_LOCALS; print_function_variables(); break; case DISPLAY_ARGS: - fprintf(fp, "display args"); + fprintf(fp, "display args for function %s", closest_symbol(local->pc)); local->flags = DISPLAY_ARGS; print_function_variables(); break; @@ -470,7 +540,8 @@ case DW_TAG_base_type: break; default: - fprintf (fp, "%c<unknown %#x>", space, tag); + if (pc->debug) + fprintf (fp, "%c<unknown %#x>", space, tag); break; } } @@ -495,7 +566,8 @@ size = dwarf_bytesize(typedie); var_addr = variable_address (fun_die,var_die, &value); if (!var_addr && !value) { - fprintf(fp, "\t Dwarf information not available"); + if (pc->debug) + fprintf(fp, "\t Dwarf information not available"); return; } @@ -544,9 +616,11 @@ static void read_vmcore(ulong addr, size_t size, void *value) { - if (readmem(addr, KVADDR, value, size, "read_vmcore", RETURN_ON_ERROR) == FALSE) - fprintf(fp, "read_vmcore error\n"); + int rc; + rc = readmem(addr, KVADDR, value, size, "read_vmcore", QUIET); + if (rc == FALSE && pc->debug) + fprintf(fp, "read_vmcore error addr:%lx\n", addr); } static ulong @@ -789,7 +863,9 @@ 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 Index: crash-4.0-8.9-build/extensions/local/local.h =================================================================== --- crash-4.0-8.9-build.orig/extensions/local/local.h 2009-05-24 18:48:12.000000000 +0530 +++ crash-4.0-8.9-build/extensions/local/local.h 2009-05-24 18:48:58.000000000 +0530 @@ -86,3 +86,6 @@ unsigned long sp; unsigned long ss; }; + +extern void *save_register(void); +extern int restore_register(void *regs); Index: crash-4.0-8.9-build/extensions/local.mk =================================================================== --- crash-4.0-8.9-build.orig/extensions/local.mk 2009-05-24 18:48:46.000000000 +0530 +++ crash-4.0-8.9-build/extensions/local.mk 2009-05-24 18:48:58.000000000 +0530 @@ -5,10 +5,12 @@ CFLAGS += -m64 endif -all: local.so - -local.so: defs.h local.c local/local.h - gcc -nostartfiles -shared $(CFLAGS) -rdynamic -o $@ local.c -ldw -D$(TARGET) - +lib-unwind: + cd local && make + +local.so: defs.h local.c lib-unwind + gcc -nostartfiles -shared $(CFLAGS) -rdynamic -o $@ local.c -fPIC -ldw -D$(TARGET) -Llocal -lunwind_dw + clean: - rm -rf local.o local.so + rm -rf local.o local.so + cd local && make clean Index: crash-4.0-8.9-build/extensions/local/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ crash-4.0-8.9-build/extensions/local/Makefile 2009-05-24 18:48:58.000000000 +0530 @@ -0,0 +1,33 @@ +TARGETS = libunwind_dw.a + +CFLAGS += -O3 -g -fPIC -Wall +ifeq ($(TARGET), PPC64) + CFLAGS += -m64 +endif + +CFILES = unwind_dw.c + +OFILES = $(CFILES:.c=.o) + +HFILES = unwind_dw.h + +all: link default + +link: + @if [ ! -f defs.h ]; then \ + ln -s ../defs.h; fi + +default: $(TARGETS) + +unwind_dw.o: unwind_dw.c unwind_dw.h + $(CC) $(CFLAGS) -c unwind_dw.c -D$(TARGET) + +$(CFILES): $(HFILES) + +$(TARGETS): $(OFILES) + $(AR) ccurl $(TARGETS) $(OFILES) + +clean: + -/bin/rm -f *.o $(TARGETS) + +clobber: clean Index: crash-4.0-8.9-build/extensions/local/unwind_dw.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ crash-4.0-8.9-build/extensions/local/unwind_dw.c 2009-05-24 18:50:07.000000000 +0530 @@ -0,0 +1,771 @@ +/* unwind_dw.c - Dwarf stack unwind interface + * + * Copyright (c) International Business Machines Corp., 2001 + * + * 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_struct *unwind_info; + +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; +} + +/* Process the CIE & FDE instructions and update the infop structure */ +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; + case DW_CFA_extended: /* Process extended op */ + break; + default: + fprintf(fp, "unknown op: 0x%x\n", pr_op); + inst++; + 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; +} + +/* Read the DEBUG_FRAME (".debug_frame") ELF Section */ +char *process(Elf *elfp, int fd, int *size, int is_64) +{ + int index; + size_t str_idx; + Elf64_Shdr *symbol_shdr64, *shdr64; + Elf32_Shdr *symbol_shdr32, *shdr32; + Elf_Scn *secn; + size_t nr_sect; + char *strtab = NULL; + size_t offset; + char *buff = NULL; + + elf_getshstrndx(elfp, &str_idx); + elf_getshnum(elfp, &nr_sect); + + secn = elf_getscn(elfp, str_idx); + if (secn == NULL) + return NULL; + + if (is_64) { + symbol_shdr64 = elf64_getshdr(secn); + if (symbol_shdr64->sh_type == SHT_STRTAB) { + strtab = malloc(symbol_shdr64->sh_size); + + offset = lseek(fd, 0, SEEK_CUR); + if (lseek(fd, symbol_shdr64->sh_offset, SEEK_SET) < 0) { + fprintf(fp, "lseek error\n"); + return NULL; + } + + if (read(fd, strtab, symbol_shdr64->sh_size) != symbol_shdr64->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; + + shdr64 = elf64_getshdr(secn); + + if (strcmp(strtab + shdr64->sh_name, DEBUG_FRAME)) + continue; + buff = malloc(shdr64->sh_size); + if (!buff) { + fprintf(fp, "can't allocate memory for buff\n"); + return NULL; + } + + if (lseek(fd, shdr64->sh_offset, SEEK_SET) < 0) { + fprintf(fp, "lseek"); + return NULL; + } + if (read(fd, buff, shdr64->sh_size) != shdr64->sh_size) { + fprintf(fp, "read error\n"); + return NULL; + } + *size = shdr64->sh_size; + break; + + index++; + } while (index++ < nr_sect); + } else { /* 32bit ELF */ + symbol_shdr32 = elf32_getshdr(secn); + if (symbol_shdr32->sh_type == SHT_STRTAB) { + strtab = malloc(symbol_shdr32->sh_size); + + offset = lseek(fd, 0, SEEK_CUR); + if (lseek(fd, symbol_shdr32->sh_offset, SEEK_SET) < 0) { + fprintf(fp, "lseek error\n"); + return NULL; + } + + if (read(fd, strtab, symbol_shdr32->sh_size) != symbol_shdr32->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; + + shdr32 = elf32_getshdr(secn); + + if (strcmp(strtab + shdr32->sh_name, DEBUG_FRAME)) + continue; + buff = malloc(shdr32->sh_size); + if (!buff) { + fprintf(fp, "can't allocate memory for buff\n"); + return NULL; + } + + if (lseek(fd, shdr32->sh_offset, SEEK_SET) < 0) { + fprintf(fp, "lseek"); + return NULL; + } + if (read(fd, buff, shdr32->sh_size) != shdr32->sh_size) { + fprintf(fp, "read error\n"); + return NULL; + } + *size = shdr32->sh_size; + break; + + index++; + } while (index++ < nr_sect); + + } + + free(strtab); + return buff; +} + +/* Get the register set related to 'address' */ +uint64_t unwind_dw(uint64_t address) +{ + 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) + 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) + local->assign_register(info.oper1, info.oper2); + } + + if (!found) { /* End of instruction seqeuence */ + if (address < fdep->initial_location + fdep->address_range) + found = 1; + local->assign_register(SP, info.cfa_address); + } + } + } + } + + return return_address; +} + +/* Save complete backtrace & register info from current pc */ +int unwind_dw_info_init(ulong pc) +{ + void *regs; + struct unwind_struct *up; + ulong addr, curr; + + regs = save_register(); /* save current registers */ + addr = pc; + + if (unwind_info) { + struct unwind_struct *next; + + for (up = unwind_info; up; ) { + next = up->next; + free(up); + up = next; + } + unwind_info = NULL; + } + + unwind_info = malloc(sizeof(struct unwind_struct)); + up = unwind_info; + + if (up == NULL) { + fprintf(fp, "Unable to allocate memory for unwind info " + " unwinding functionality is disabled\n"); + return 0; + } + + curr = pc; + + up->pc = pc; + up->regs = save_register(); + + addr = unwind_dw(pc); + up->ret_addr = addr; + + do { + pc = addr; + up->next = malloc(sizeof(struct unwind_struct)); + 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); + up->ret_addr = addr; + + up->next = NULL; + + } while (addr); + + restore_register(unwind_info->regs); /* restore current registers */ + free(regs); + + local->pc = curr; + + return 1; +} + +/* Initialize unwind dwarf interface */ +int unwind_dw_init(int fd, Elf *elfp) +{ + 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; + + ciep = NULL; + fdep = NULL; + + if (elf64_getehdr(elfp)) + elf_buff = buff = process(elfp, fd, &sh_size, 1); + else if (elf32_getehdr(elfp)) + elf_buff = buff = process(elfp, fd, &sh_size, 0); + + if (!buff) { + fprintf(fp, "%s section not present?\n", DEBUG_FRAME); + 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 */ + if (get_netdump_arch() == EM_386) { + location = get4b(readp); + readp += 4; + range = get4b(readp); + readp += 4; + } else { + 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; + } + } + + return 1; +} + +/* Unwind up from current pc */ +void unwind_dw_up(uint64_t pc) +{ + struct unwind_struct *up; + + up = unwind_info; + + while (up) { + if (up->pc == pc) { + pc = up->ret_addr; + break; + } + up = up->next; + } + + up = unwind_info; + 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 %s\n", closest_symbol(pc)); +} + +/* Unwind down from current pc */ +void unwind_dw_down(uint64_t pc) +{ + uint64_t ret_addr; + struct unwind_struct *up; + + up = unwind_info; + + while (up) { + if (up->ret_addr == pc) + break; + 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 %s\n", closest_symbol(up->pc)); + } +} + +/* Cleanup memory allocated by unwind interface */ +void unwind_dw_fini(void) +{ + struct cie_info *ciep, *cnext; + struct fde_info *fdep, *fnext; + + struct unwind_struct *up; + + 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; + } + + if (unwind_info) { + struct unwind_struct *next; + + for (up = unwind_info; up; ) { + next = up->next; + free(up); + up = next; + } + unwind_info = NULL; + } +} Index: crash-4.0-8.9-build/extensions/local/unwind_dw.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ crash-4.0-8.9-build/extensions/local/unwind_dw.h 2009-05-24 18:48:58.000000000 +0530 @@ -0,0 +1,160 @@ +/* unwind_dw.h - Dwarf stack unwind interface + * + * Copyright (c) International Business Machines Corp., 2001 + * + * 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); +int unwind_dw_info_init(ulong pc); +void unwind_dw_up(uint64_t pc); +void unwind_dw_down(uint64_t pc); +void unwind_dw_fini(void); + +/* Common Information Entry structure to hold CIE info and instructions */ +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; +}; + +/* Frame Description Entry structure to hold FDE info and instructions */ +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_struct { + ulong pc; + ulong ret_addr; + void *regs; + struct unwind_struct *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));
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility