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
[email protected]
https://www.redhat.com/mailman/listinfo/crash-utility