[PATCH 2/2] Provide stack unwinding feature

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

 



>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

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

 

Powered by Linux