[PATCH 2/3] Provide stack unwinding

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




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

[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux