[Crash-utility] [RFC] Crash patch for DWARF CFI based unwind support

Rachita Kothiyal rachita at in.ibm.com
Mon Oct 16 14:30:03 UTC 2006


Hi Dave

The following patch adds support for DWARF CFI based stack unwinding
for crash. Since this method uses the call frame instructions for 
unwinding, it generates better backtraces than the existing backtrace
mechanism. So when we have the unwind info available, this new method 
will be called, else we fall back to the existing mechanism.

There still are a couple of things which need to be done, viz
1. Extend to obtaining unwind info from modules as well(currently
   doing only for the kernel)
2. Currently reading the unwind info from eh_frame section only(ie
   __start_unwind to __end_unwind). Need to add facility to read from
   the .debug_frame(if .debug_frame is present in cases where .eh_frame
   is absent. Will have to read from the vmlinux if we want to read the
   .debug_frame info)
3. Add FRAME_POINTER support.

Please provide your suggestions and comments.

Thanks
Rachita




Signed-off-by: Rachita Kothiyal <rachita at in.ibm.com>
---

 Makefile        |   13 -
 netdump.c       |   24 +
 unwind_x86_64.c |  699 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 unwind_x86_64.h |  135 ++++++++++
 x86_64.c        |   57 ++++
 5 files changed, 919 insertions(+), 9 deletions(-)

diff -puN x86_64.c~crash-dwarf-unwind x86_64.c
--- crash-4.0-3.7/x86_64.c~crash-dwarf-unwind	2006-10-16 18:14:24.907025568 +0530
+++ crash-4.0-3.7-rachita/x86_64.c	2006-10-16 18:14:41.231543864 +0530
@@ -14,6 +14,7 @@
  * GNU General Public License for more details.
  */
 #include "defs.h"
+#include "unwind_x86_64.h"
 
 #ifdef X86_64
 
@@ -81,6 +82,7 @@ static ulong x86_64_xen_kdump_page_mfn(u
 static void x86_64_debug_dump_page(FILE *, char *, char *);
 static void x86_64_get_xendump_regs(struct xendump_data *, struct bt_info *, ulong *, ulong *);
 static ulong x86_64_xendump_panic_task(struct xendump_data *);
+static int dwarf_backtrace(struct bt_info *, ulong);
 
 struct machine_specific x86_64_machine_specific = { 0 };
 
@@ -245,6 +247,9 @@ x86_64_init(int when)
 		STRUCT_SIZE_INIT(user_regs_struct, "user_regs_struct");
 		x86_64_cpu_pda_init();
 		x86_64_ist_init();
+		if (symbol_exists("__start_unwind") &&
+	                        symbol_exists("__end_unwind"))
+                        init_unwind_table();
                 if ((machdep->machspec->irqstack = (char *)
 		    malloc(machdep->machspec->stkinfo.isize)) == NULL)
                         error(FATAL, "cannot malloc irqstack space.");
@@ -2170,6 +2175,9 @@ in_exception_stack:
                 }
 
 		stacktop = bt->stacktop - SIZE(pt_regs);
+		
+		if (has_unwind_info && !done)
+			done = dwarf_backtrace(bt, stacktop);
 
         	for (i = (rsp - bt->stackbase)/sizeof(ulong);
 	     	    !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) {
@@ -2253,6 +2261,9 @@ in_exception_stack:
 
 		stacktop = bt->stacktop - 64; /* from kernel code */
 
+		if (has_unwind_info && !done)
+			done = dwarf_backtrace(bt, stacktop);
+
                 for (i = (rsp - bt->stackbase)/sizeof(ulong);
                     !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) {
 
@@ -2336,7 +2347,7 @@ in_exception_stack:
 	/*
 	 *  For a normally blocked task, hand-create the first level.
 	 */
-        if (!done && 
+        if (!done && !has_unwind_info &&
 	    !(bt->flags & (BT_TEXT_SYMBOLS|BT_EXCEPTION_STACK|BT_IRQSTACK)) &&
 	    STREQ(closest_symbol(bt->instptr), "thread_return")) {
 		bt->flags |= BT_SCHEDULE;
@@ -2375,6 +2386,9 @@ in_exception_stack:
 	/*
 	 *  Walk the process stack.  
 	 */
+	if (has_unwind_info && !done)
+		done = dwarf_backtrace(bt, bt->stacktop);
+
         for (i = (rsp - bt->stackbase)/sizeof(ulong);
 	     !done && (rsp < bt->stacktop); i++, rsp += sizeof(ulong)) {
 
@@ -4370,4 +4384,45 @@ generic:
                 }
 	}
 }
+
+static int dwarf_backtrace(struct bt_info *bt, ulong stacktop)
+{
+	int n = 0;
+	unsigned long bp, offset;
+	struct syment *sp;
+	char *name;
+	struct unwind_frame_info *frame = malloc(sizeof(struct unwind_frame_info));
+
+	frame->regs.rsp = bt->stkptr;
+	frame->regs.rip = bt->instptr;
+
+	/* read rbp from stack for non active tasks */
+	if (!(bt->flags & BT_DUMPFILE_SEARCH) ) {
+		readmem(frame->regs.rsp, KVADDR, &bp,
+	                sizeof(unsigned long), "reading bp", FAULT_ON_ERROR);
+		frame->regs.rbp = bp;
+	}
+
+	sp = value_search(UNW_PC(frame), &offset);
+	/*
+	 * If offset is zero, it means we have crossed over to the next
+	 *  function. Recalculate by adjusting the text address
+	 */
+	if (!offset)
+		sp = value_search(UNW_PC(frame) - 1, &offset);
+		
+        name = sp->name;
+	fprintf(fp, "#0 [%016lx] %s at %016lx \n", UNW_SP(frame), name, UNW_PC(frame));
+
+       	while ((UNW_SP(frame) < stacktop)
+				&& !unwind(frame) && UNW_PC(frame)) {
+               	n++;
+		sp = value_search(UNW_PC(frame), &offset);
+	        name = sp->name;
+		fprintf(fp, "#%d [%016lx] %s at %016lx \n", n, UNW_SP(frame), name, UNW_PC(frame));
+       	}
+	free(frame);
+	return TRUE;
+}
+
 #endif  /* X86_64 */ 
diff -puN /dev/null unwind_x86_64.c
--- /dev/null	2006-07-24 19:06:05.520445648 +0530
+++ crash-4.0-3.7-rachita/unwind_x86_64.c	2006-10-16 18:21:12.131118096 +0530
@@ -0,0 +1,699 @@
+/*
+ * Support for genarating DWARF CFI based backtraces.
+ * Borrowed heavily from the kernel's implementation of unwinding using the
+ * DWARF CFI written by Jan Beulich
+ */
+
+#include "unwind_x86_64.h"
+#include "defs.h"
+#define MAX_STACK_DEPTH 8
+
+void *unwind_table;
+int unwind_table_size = 0;
+int has_unwind_info = 0;
+
+static const struct {
+	unsigned offs:BITS_PER_LONG / 2;
+	unsigned width:BITS_PER_LONG / 2;
+} reg_info[] = {
+	UNW_REGISTER_INFO
+};
+
+#undef PTREGS_INFO
+#undef EXTRA_INFO
+
+#ifndef REG_INVALID
+#define REG_INVALID(r) (reg_info[r].width == 0)
+#endif
+
+#define DW_CFA_nop                          0x00
+#define DW_CFA_set_loc                      0x01
+#define DW_CFA_advance_loc1                 0x02
+#define DW_CFA_advance_loc2                 0x03
+#define DW_CFA_advance_loc4                 0x04
+#define DW_CFA_offset_extended              0x05
+#define DW_CFA_restore_extended             0x06
+#define DW_CFA_undefined                    0x07
+#define DW_CFA_same_value                   0x08
+#define DW_CFA_register                     0x09
+#define DW_CFA_remember_state               0x0a
+#define DW_CFA_restore_state                0x0b
+#define DW_CFA_def_cfa                      0x0c
+#define DW_CFA_def_cfa_register             0x0d
+#define DW_CFA_def_cfa_offset               0x0e
+#define DW_CFA_def_cfa_expression           0x0f
+#define DW_CFA_expression                   0x10
+#define DW_CFA_offset_extended_sf           0x11
+#define DW_CFA_def_cfa_sf                   0x12
+#define DW_CFA_def_cfa_offset_sf            0x13
+#define DW_CFA_val_offset                   0x14
+#define DW_CFA_val_offset_sf                0x15
+#define DW_CFA_val_expression               0x16
+#define DW_CFA_lo_user                      0x1c
+#define DW_CFA_GNU_window_save              0x2d
+#define DW_CFA_GNU_args_size                0x2e
+#define DW_CFA_GNU_negative_offset_extended 0x2f
+#define DW_CFA_hi_user                      0x3f
+
+#define DW_EH_PE_FORM     0x07
+#define DW_EH_PE_native   0x00
+#define DW_EH_PE_leb128   0x01
+#define DW_EH_PE_data2    0x02
+#define DW_EH_PE_data4    0x03
+#define DW_EH_PE_data8    0x04
+#define DW_EH_PE_signed   0x08
+#define DW_EH_PE_ADJUST   0x70
+#define DW_EH_PE_abs      0x00
+#define DW_EH_PE_pcrel    0x10
+#define DW_EH_PE_textrel  0x20
+#define DW_EH_PE_datarel  0x30
+#define DW_EH_PE_funcrel  0x40
+#define DW_EH_PE_aligned  0x50
+#define DW_EH_PE_indirect 0x80
+#define DW_EH_PE_omit     0xff
+
+#define min(x,y) ({ \
+        typeof(x) _x = (x);     \
+        typeof(y) _y = (y);     \
+        (void) (&_x == &_y);            \
+        _x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+        typeof(x) _x = (x);     \
+        typeof(y) _y = (y);     \
+        (void) (&_x == &_y);            \
+        _x > _y ? _x : _y; })
+#define STACK_LIMIT(ptr)     (((ptr) - 1) & ~(THREAD_SIZE - 1))
+
+typedef unsigned long uleb128_t;
+typedef   signed long sleb128_t;
+
+struct unwind_item {
+	enum item_location {
+		Nowhere,
+		Memory,
+		Register,
+		Value
+	} where;
+	uleb128_t value;
+};
+
+struct unwind_state {
+	uleb128_t loc, org;
+	const u8 *cieStart, *cieEnd;
+	uleb128_t codeAlign;
+	sleb128_t dataAlign;
+	struct cfa {
+		uleb128_t reg, offs;
+	} cfa;
+	struct unwind_item regs[ARRAY_SIZE(reg_info)];
+	unsigned stackDepth:8;
+	unsigned version:8;
+	const u8 *label;
+	const u8 *stack[MAX_STACK_DEPTH];
+};
+
+static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
+
+static uleb128_t get_uleb128(const u8 **pcur, const u8 *end)
+{
+	const u8 *cur = *pcur;
+	uleb128_t value;
+	unsigned shift;
+
+	for (shift = 0, value = 0; cur < end; shift += 7) {
+		if (shift + 7 > 8 * sizeof(value)
+		    && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
+			cur = end + 1;
+			break;
+		}
+		value |= (uleb128_t)(*cur & 0x7f) << shift;
+		if (!(*cur++ & 0x80))
+			break;
+	}
+	*pcur = cur;
+
+	return value;
+}
+
+static sleb128_t get_sleb128(const u8 **pcur, const u8 *end)
+{
+	const u8 *cur = *pcur;
+	sleb128_t value;
+	unsigned shift;
+
+	for (shift = 0, value = 0; cur < end; shift += 7) {
+		if (shift + 7 > 8 * sizeof(value)
+		    && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
+			cur = end + 1;
+			break;
+		}
+		value |= (sleb128_t)(*cur & 0x7f) << shift;
+		if (!(*cur & 0x80)) {
+			value |= -(*cur++ & 0x40) << shift;
+			break;
+		}
+	}
+	*pcur = cur;
+
+	return value;
+}
+
+static unsigned long read_pointer(const u8 **pLoc,
+                                  const void *end,
+                                  signed ptrType)
+{
+	unsigned long value = 0;
+	union {
+		const u8 *p8;
+		const u16 *p16u;
+		const s16 *p16s;
+		const u32 *p32u;
+		const s32 *p32s;
+		const unsigned long *pul;
+	} ptr;
+
+	if (ptrType < 0 || ptrType == DW_EH_PE_omit)
+		return 0;
+	ptr.p8 = *pLoc;
+	switch(ptrType & DW_EH_PE_FORM) {
+	case DW_EH_PE_data2:
+		if (end < (const void *)(ptr.p16u + 1))
+			return 0;
+		if(ptrType & DW_EH_PE_signed)
+			value = get_unaligned(ptr.p16s++);
+		else
+			value = get_unaligned(ptr.p16u++);
+		break;
+	case DW_EH_PE_data4:
+#ifdef CONFIG_64BIT
+		if (end < (const void *)(ptr.p32u + 1))
+			return 0;
+		if(ptrType & DW_EH_PE_signed)
+			value = get_unaligned(ptr.p32s++);
+		else
+			value = get_unaligned(ptr.p32u++);
+		break;
+	case DW_EH_PE_data8:
+		BUILD_BUG_ON(sizeof(u64) != sizeof(value));
+#else
+		BUILD_BUG_ON(sizeof(u32) != sizeof(value));
+#endif
+	case DW_EH_PE_native:
+		if (end < (const void *)(ptr.pul + 1))
+			return 0;
+		value = get_unaligned(ptr.pul++);
+		break;
+	case DW_EH_PE_leb128:
+		BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value));
+		value = ptrType & DW_EH_PE_signed
+		        ? get_sleb128(&ptr.p8, end)
+		        : get_uleb128(&ptr.p8, end);
+		if ((const void *)ptr.p8 > end)
+			return 0;
+		break;
+	default:
+		return 0;
+	}
+	switch(ptrType & DW_EH_PE_ADJUST) {
+	case DW_EH_PE_abs:
+		break;
+	case DW_EH_PE_pcrel:
+		value += (unsigned long)*pLoc;
+		break;
+	default:
+		return 0;
+	}
+
+/*	TBD
+	if ((ptrType & DW_EH_PE_indirect)
+	    && __get_user(value, (unsigned long *)value))
+		return 0;
+*/
+	*pLoc = ptr.p8;
+
+	return value;
+}
+
+static signed fde_pointer_type(const u32 *cie)
+{
+	const u8 *ptr = (const u8 *)(cie + 2);
+	unsigned version = *ptr;
+
+	if (version != 1)
+		return -1; /* unsupported */
+	if (*++ptr) {
+		const char *aug;
+		const u8 *end = (const u8 *)(cie + 1) + *cie;
+		uleb128_t len;
+
+		/* check if augmentation size is first (and thus present) */
+		if (*ptr != 'z')
+			return -1;
+		/* check if augmentation string is nul-terminated */
+		if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL)
+			return -1;
+		++ptr; /* skip terminator */
+		get_uleb128(&ptr, end); /* skip code alignment */
+		get_sleb128(&ptr, end); /* skip data alignment */
+		/* skip return address column */
+		version <= 1 ? (void)++ptr : (void)get_uleb128(&ptr, end);
+		len = get_uleb128(&ptr, end); /* augmentation length */
+		if (ptr + len < ptr || ptr + len > end)
+			return -1;
+		end = ptr + len;
+		while (*++aug) {
+			if (ptr >= end)
+				return -1;
+			switch(*aug) {
+			case 'L':
+				++ptr;
+				break;
+			case 'P': {
+					signed ptrType = *ptr++;
+
+					if (!read_pointer(&ptr, end, ptrType) || 					     ptr > end)
+						return -1;
+				}
+				break;
+			case 'R':
+				return *ptr;
+			default:
+				return -1;
+			}
+		}
+	}
+	return DW_EH_PE_native|DW_EH_PE_abs;
+}
+
+static int advance_loc(unsigned long delta, struct unwind_state *state)
+{
+	state->loc += delta * state->codeAlign;
+
+	return delta > 0;
+}
+
+static void set_rule(uleb128_t reg,
+                     enum item_location where,
+                     uleb128_t value,
+                     struct unwind_state *state)
+{
+	if (reg < ARRAY_SIZE(state->regs)) {
+		state->regs[reg].where = where;
+		state->regs[reg].value = value;
+	}
+}
+
+static int processCFI(const u8 *start,
+                      const u8 *end,
+                      unsigned long targetLoc,
+                      signed ptrType,
+                      struct unwind_state *state)
+{
+	union {
+		const u8 *p8;
+		const u16 *p16;
+		const u32 *p32;
+	} ptr;
+	int result = 1;
+
+	if (start != state->cieStart) {
+		state->loc = state->org;
+		result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, 				    state);
+		if (targetLoc == 0 && state->label == NULL)
+			return result;
+	}
+	for (ptr.p8 = start; result && ptr.p8 < end; ) {
+		switch(*ptr.p8 >> 6) {
+			uleb128_t value;
+
+		case 0:
+			switch(*ptr.p8++) {
+			case DW_CFA_nop:
+				break;
+			case DW_CFA_set_loc:
+				if ((state->loc = read_pointer(&ptr.p8, end,
+								ptrType)) == 0)
+					result = 0;
+				break;
+			case DW_CFA_advance_loc1:
+				result = ptr.p8 < end && advance_loc(*ptr.p8++, 					 state);
+				break;
+			case DW_CFA_advance_loc2:
+				result = ptr.p8 <= end + 2
+				         && advance_loc(*ptr.p16++, state);
+				break;
+			case DW_CFA_advance_loc4:
+				result = ptr.p8 <= end + 4
+				         && advance_loc(*ptr.p32++, state);
+				break;
+			case DW_CFA_offset_extended:
+				value = get_uleb128(&ptr.p8, end);
+				set_rule(value, Memory,
+					get_uleb128(&ptr.p8, end), state);
+				break;
+			case DW_CFA_val_offset:
+				value = get_uleb128(&ptr.p8, end);
+				set_rule(value, Value,
+					get_uleb128(&ptr.p8, end), state);
+				break;
+			case DW_CFA_offset_extended_sf:
+				value = get_uleb128(&ptr.p8, end);
+				set_rule(value, Memory,
+					get_sleb128(&ptr.p8, end), state);
+				break;
+			case DW_CFA_val_offset_sf:
+				value = get_uleb128(&ptr.p8, end);
+				set_rule(value, Value,
+					get_sleb128(&ptr.p8, end), state);
+				break;
+			case DW_CFA_restore_extended:
+			case DW_CFA_undefined:
+			case DW_CFA_same_value:
+				set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, 					state);
+				break;
+			case DW_CFA_register:
+				value = get_uleb128(&ptr.p8, end);
+				set_rule(value, Register,
+				         get_uleb128(&ptr.p8, end), state);
+				break;
+			case DW_CFA_remember_state:
+				if (ptr.p8 == state->label) {
+					state->label = NULL;
+					return 1;
+				}
+				if (state->stackDepth >= MAX_STACK_DEPTH)
+					return 0;
+				state->stack[state->stackDepth++] = ptr.p8;
+				break;
+			case DW_CFA_restore_state:
+				if (state->stackDepth) {
+					const uleb128_t loc = state->loc;
+					const u8 *label = state->label;
+
+					state->label = state->stack[state->stackDepth - 1];
+					memcpy(&state->cfa, &badCFA, sizeof(state->cfa));
+					memset(state->regs, 0, sizeof(state->regs));
+					state->stackDepth = 0;
+					result = processCFI(start, end, 0, ptrType, state);
+					state->loc = loc;
+					state->label = label;
+				} else
+					return 0;
+				break;
+			case DW_CFA_def_cfa:
+				state->cfa.reg = get_uleb128(&ptr.p8, end);
+				/*nobreak*/
+			case DW_CFA_def_cfa_offset:
+				state->cfa.offs = get_uleb128(&ptr.p8, end);
+				break;
+			case DW_CFA_def_cfa_sf:
+				state->cfa.reg = get_uleb128(&ptr.p8, end);
+				/*nobreak*/
+			case DW_CFA_def_cfa_offset_sf:
+				state->cfa.offs = get_sleb128(&ptr.p8, end)
+				                  * state->dataAlign;
+				break;
+			case DW_CFA_def_cfa_register:
+				state->cfa.reg = get_uleb128(&ptr.p8, end);
+				break;
+			/*todo case DW_CFA_def_cfa_expression: */
+			/*todo case DW_CFA_expression: */
+			/*todo case DW_CFA_val_expression: */
+			case DW_CFA_GNU_args_size:
+				get_uleb128(&ptr.p8, end);
+				break;
+			case DW_CFA_GNU_negative_offset_extended:
+				value = get_uleb128(&ptr.p8, end);
+				set_rule(value, Memory, (uleb128_t)0 -
+				         get_uleb128(&ptr.p8, end), state);
+				break;
+			case DW_CFA_GNU_window_save:
+			default:
+				result = 0;
+				break;
+			}
+			break;
+		case 1:
+			result = advance_loc(*ptr.p8++ & 0x3f, state);
+			break;
+		case 2:
+			value = *ptr.p8++ & 0x3f;
+			set_rule(value, Memory, get_uleb128(&ptr.p8, end),
+				 state);
+			break;
+		case 3:
+			set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
+			break;
+		}
+		if (ptr.p8 > end)
+			result = 0;
+		if (result && targetLoc != 0 && targetLoc < state->loc)
+			return 1;
+	}
+
+	return result
+	   && ptr.p8 == end
+	   && (targetLoc == 0
+	    || (/*todo While in theory this should apply, gcc in practice omits
+	          everything past the function prolog, and hence the location
+	          never reaches the end of the function.
+	        targetLoc < state->loc &&*/ state->label == NULL));
+}
+
+
+/* Unwind to previous to frame.  Returns 0 if successful, negative
+ * number in case of an error. */
+int unwind(struct unwind_frame_info *frame)
+{
+#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
+	const u32 *fde = NULL, *cie = NULL;
+	const u8 *ptr = NULL, *end = NULL;
+	unsigned long startLoc = 0, endLoc = 0, cfa;
+	unsigned i;
+	signed ptrType = -1;
+	uleb128_t retAddrReg = 0;
+	struct unwind_table *table;
+	struct unwind_state state;
+	u64 reg_ptr = 0;
+
+	if (UNW_PC(frame) == 0)
+		return -EINVAL;
+
+	unsigned long tableSize = unwind_table_size;
+
+		for (fde = unwind_table;
+		     tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde;
+		     tableSize -= sizeof(*fde) + *fde,
+		     fde += 1 + *fde / sizeof(*fde)) {
+			if (!*fde || (*fde & (sizeof(*fde) - 1)))
+				break;
+			if (!fde[1])
+				continue; /* this is a CIE */
+			if ((fde[1] & (sizeof(*fde) - 1))
+			    || fde[1] > (unsigned long)(fde + 1)
+			                - (unsigned long)unwind_table)
+				continue; /* this is not a valid FDE */
+			cie = fde + 1 - fde[1] / sizeof(*fde);
+			if (*cie <= sizeof(*cie) + 4
+			    || *cie >= fde[1] - sizeof(*fde)
+			    || (*cie & (sizeof(*cie) - 1))
+			    || cie[1]
+			    || (ptrType = fde_pointer_type(cie)) < 0) {
+				cie = NULL; /* this is not a (valid) CIE */
+				continue;
+			}
+			ptr = (const u8 *)(fde + 2);
+			startLoc = read_pointer(&ptr,
+			                        (const u8 *)(fde + 1) + *fde,
+			                        ptrType);
+			endLoc = startLoc
+			         + read_pointer(&ptr,
+			                        (const u8 *)(fde + 1) + *fde,
+			                        ptrType & DW_EH_PE_indirect
+			                        ? ptrType
+			                        : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed));
+			if (UNW_PC(frame) >= startLoc && UNW_PC(frame) < endLoc)
+				break;
+			cie = NULL;
+		}
+
+	if (cie != NULL) {
+		memset(&state, 0, sizeof(state));
+		state.cieEnd = ptr; /* keep here temporarily */
+		ptr = (const u8 *)(cie + 2);
+		end = (const u8 *)(cie + 1) + *cie;
+		if ((state.version = *ptr) != 1)
+			cie = NULL; /* unsupported version */
+		else if (*++ptr) {
+			/* check if augmentation size is first (and thus present) */
+			if (*ptr == 'z') {
+				/* check for ignorable (or already handled)
+				 * nul-terminated augmentation string */
+				while (++ptr < end && *ptr)
+					if (strchr("LPR", *ptr) == NULL)
+						break;
+			}
+			if (ptr >= end || *ptr)
+				cie = NULL;
+		}
+		++ptr;
+	}
+	if (cie != NULL) {
+		/* get code aligment factor */
+		state.codeAlign = get_uleb128(&ptr, end);
+		/* get data aligment factor */
+		state.dataAlign = get_sleb128(&ptr, end);
+		if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
+			cie = NULL;
+		else {
+			retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end);
+			/* skip augmentation */
+			if (((const char *)(cie + 2))[1] == 'z')
+				ptr += get_uleb128(&ptr, end);
+			if (ptr > end
+			   || retAddrReg >= ARRAY_SIZE(reg_info)
+			   || REG_INVALID(retAddrReg)
+			   || reg_info[retAddrReg].width != sizeof(unsigned long))
+				cie = NULL;
+		}
+	}
+	if (cie != NULL) {
+		state.cieStart = ptr;
+		ptr = state.cieEnd;
+		state.cieEnd = end;
+		end = (const u8 *)(fde + 1) + *fde;
+		/* skip augmentation */
+		if (((const char *)(cie + 2))[1] == 'z') {
+			uleb128_t augSize = get_uleb128(&ptr, end);
+
+			if ((ptr += augSize) > end)
+				fde = NULL;
+		}
+	}
+	if (cie == NULL || fde == NULL)
+		return -ENXIO;
+
+	state.org = startLoc;
+	memcpy(&state.cfa, &badCFA, sizeof(state.cfa));
+	/* process instructions */
+	if (!processCFI(ptr, end, UNW_PC(frame), ptrType, &state)
+	   || state.loc > endLoc
+	   || state.regs[retAddrReg].where == Nowhere
+	   || state.cfa.reg >= ARRAY_SIZE(reg_info)
+	   || reg_info[state.cfa.reg].width != sizeof(unsigned long)
+	   || state.cfa.offs % sizeof(unsigned long)) {
+		return -EIO;
+		}
+	/* update frame */
+	cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
+	startLoc = min((unsigned long)UNW_SP(frame), cfa);
+	endLoc = max((unsigned long)UNW_SP(frame), cfa);
+	if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) {
+		startLoc = min(STACK_LIMIT(cfa), cfa);
+		endLoc = max(STACK_LIMIT(cfa), cfa);
+	}
+#ifndef CONFIG_64BIT
+# define CASES CASE(8); CASE(16); CASE(32)
+#else
+# define CASES CASE(8); CASE(16); CASE(32); CASE(64)
+#endif
+	for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
+		if (REG_INVALID(i)) {
+			if (state.regs[i].where == Nowhere)
+				continue;
+			return -EIO;
+		}
+		switch(state.regs[i].where) {
+		default:
+			break;
+		case Register:
+			if (state.regs[i].value >= ARRAY_SIZE(reg_info)
+			   || REG_INVALID(state.regs[i].value)
+			   || reg_info[i].width > reg_info[state.regs[i].value].width){
+				return -EIO;
+	}
+			switch(reg_info[state.regs[i].value].width) {
+#define CASE(n) \
+			case sizeof(u##n): \
+				state.regs[i].value = FRAME_REG(state.regs[i].value, \
+				                                const u##n); \
+				break
+			CASES;
+#undef CASE
+			default:
+				return -EIO;
+			}
+			break;
+		}
+	}
+	for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
+		if (REG_INVALID(i))
+			continue;
+		switch(state.regs[i].where) {
+		case Nowhere:
+			if (reg_info[i].width != sizeof(UNW_SP(frame))
+			   || &FRAME_REG(i, __typeof__(UNW_SP(frame)))
+			      != &UNW_SP(frame))
+				continue;
+			UNW_SP(frame) = cfa;
+			break;
+		case Register:
+			switch(reg_info[i].width) {
+#define CASE(n) case sizeof(u##n): \
+				FRAME_REG(i, u##n) = state.regs[i].value; \
+				break
+			CASES;
+#undef CASE
+			default:
+				return -EIO;
+			}
+			break;
+		case Value:
+			if (reg_info[i].width != sizeof(unsigned long)){
+				return -EIO;}
+			FRAME_REG(i, unsigned long) = cfa + state.regs[i].value
+			                                    * state.dataAlign;
+			break;
+		case Memory: {
+				unsigned long addr = cfa + state.regs[i].value
+				                           * state.dataAlign;
+				if ((state.regs[i].value * state.dataAlign)
+				    % sizeof(unsigned long)
+				    || addr < startLoc
+				    || addr + sizeof(unsigned long) < addr
+				    || addr + sizeof(unsigned long) > endLoc){
+					return -EIO;}
+				switch(reg_info[i].width) {
+#define CASE(n)     case sizeof(u##n): \
+					readmem(addr, KVADDR, &reg_ptr,sizeof(u##n), "register", RETURN_ON_ERROR|QUIET); \
+					FRAME_REG(i, u##n) = (u##n)reg_ptr;\
+					break
+				CASES;
+#undef CASE
+				default:
+					return -EIO;
+				}
+			}
+			break;
+		}
+	}
+	return 0;
+#undef CASES
+#undef FRAME_REG
+}
+EXPORT_SYMBOL(unwind);
+
+void init_unwind_table()
+{
+	unwind_table_size = symbol_value("__end_unwind") -                                     		    symbol_value("__start_unwind");
+	unwind_table = malloc(unwind_table_size);
+	if(readmem (symbol_value("__start_unwind"), KVADDR, unwind_table,
+                    unwind_table_size, "unwind table", RETURN_ON_ERROR))
+		has_unwind_info = 1;
+}
+
+void free_unwind_table()
+{
+	free(unwind_table);
+}
diff -puN /dev/null unwind_x86_64.h
--- /dev/null	2006-07-24 19:06:05.520445648 +0530
+++ crash-4.0-3.7-rachita/unwind_x86_64.h	2006-10-16 18:18:45.955340192 +0530
@@ -0,0 +1,135 @@
+#define BITS_PER_LONG 64
+#define CONFIG_64BIT 1
+#define NULL ((void *)0)
+
+typedef unsigned long size_t;
+typedef unsigned char u8;
+typedef signed short s16;
+typedef unsigned short u16;
+typedef signed int s32;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+struct pt_regs {
+        unsigned long r15;
+        unsigned long r14;
+        unsigned long r13;
+        unsigned long r12;
+        unsigned long rbp;
+        unsigned long rbx;
+/* arguments: non interrupts/non tracing syscalls only save upto here*/
+        unsigned long r11;
+        unsigned long r10;
+        unsigned long r9;
+        unsigned long r8;
+        unsigned long rax;
+        unsigned long rcx;
+        unsigned long rdx;
+        unsigned long rsi;
+        unsigned long rdi;
+        unsigned long orig_rax;
+/* end of arguments */
+/* cpu exception frame or undefined */
+        unsigned long rip;
+        unsigned long cs;
+        unsigned long eflags;
+        unsigned long rsp;
+        unsigned long ss;
+/* top of stack page */
+};
+
+struct unwind_frame_info
+{
+        struct pt_regs regs;
+};
+
+extern int unwind(struct unwind_frame_info *);
+extern void init_unwind_table(void);
+extern void free_unwind_table(void);
+extern void *unwind_table;
+extern int unwind_table_size;
+extern int has_unwind_info;
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)
+#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
+#define get_unaligned(ptr) (*(ptr))
+#define __get_user(x,ptr)  __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
+#define THREAD_ORDER 1
+#define THREAD_SIZE  (PAGE_SIZE << THREAD_ORDER)
+
+#define UNW_PC(frame)        (frame)->regs.rip
+#define UNW_SP(frame)        (frame)->regs.rsp
+#ifdef CONFIG_FRAME_POINTER
+	#define UNW_FP(frame)        (frame)->regs.rbp
+	#define FRAME_RETADDR_OFFSET 8
+	#define FRAME_LINK_OFFSET    0
+	#define STACK_BOTTOM(tsk)    (((tsk)->thread.rsp0 - 1) & ~(THREAD_SIZE - 1))
+	#define STACK_TOP(tsk)       ((tsk)->thread.rsp0)
+#endif
+
+
+#define EXTRA_INFO(f) { BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) % FIELD_SIZEOF(struct unwind_frame_info, f)) + offsetof(struct unwind_frame_info, f)/ FIELD_SIZEOF(struct unwind_frame_info, f), FIELD_SIZEOF(struct unwind_frame_info, f) }
+
+#define PTREGS_INFO(f) EXTRA_INFO(regs.f)
+
+#define UNW_REGISTER_INFO \
+	PTREGS_INFO(rax),\
+	PTREGS_INFO(rdx),\
+	PTREGS_INFO(rcx),\
+	PTREGS_INFO(rbx), \
+	PTREGS_INFO(rsi), \
+	PTREGS_INFO(rdi), \
+	PTREGS_INFO(rbp), \
+	PTREGS_INFO(rsp), \
+	PTREGS_INFO(r8), \
+	PTREGS_INFO(r9), \
+	PTREGS_INFO(r10),\
+	PTREGS_INFO(r11), \
+	PTREGS_INFO(r12), \
+	PTREGS_INFO(r13), \
+	PTREGS_INFO(r14), \
+	PTREGS_INFO(r15), \
+	PTREGS_INFO(rip)
+
+#define __get_user_nocheck(x,ptr,size)                          \
+({                                                              \
+        int __gu_err;                                           \
+        unsigned long __gu_val;                                 \
+        __get_user_size(__gu_val,(ptr),(size),__gu_err);        \
+        (x) = (__typeof__(*(ptr)))__gu_val;                     \
+        __gu_err;                                               \
+})
+
+#define __get_user_size(x,ptr,size,retval)                              \
+do {                                                                    \
+        retval = 0;                                                     \
+        __chk_user_ptr(ptr);                                            \
+        switch (size) {                                                 \
+          case 1: __get_user_asm(x,ptr,retval,"b","b","=q",-EFAULT); break;\
+          case 2: __get_user_asm(x,ptr,retval,"w","w","=r",-EFAULT); break;\
+          case 4: __get_user_asm(x,ptr,retval,"l","k","=r",-EFAULT); break;\
+          case 8: __get_user_asm(x,ptr,retval,"q","","=r",-EFAULT); break;\
+          default: (x) = __get_user_bad();                              \
+        }                                                               \
+} while (0)
+
+#define __get_user_asm(x, addr, err, itype, rtype, ltype, errno)        \
+        __asm__ __volatile__(                                   \
+                "1:     mov"itype" %2,%"rtype"1\n"              \
+                "2:\n"                                          \
+                ".section .fixup,\"ax\"\n"                      \
+                "3:     mov %3,%0\n"                            \
+                "       xor"itype" %"rtype"1,%"rtype"1\n"       \
+                "       jmp 2b\n"                               \
+                ".previous\n"                                   \
+                ".section __ex_table,\"a\"\n"                   \
+                "       .align 8\n"                             \
+                "       .quad 1b,3b\n"                          \
+                ".previous"                                     \
+                : "=r"(err), ltype (x)                          \
+                : "m"(__m(addr)), "i"(errno), "0"(err))
+
+# define __chk_user_ptr(x) (void)0
diff -puN netdump.c~crash-dwarf-unwind netdump.c
--- crash-4.0-3.7/netdump.c~crash-dwarf-unwind	2006-10-16 18:19:14.388017768 +0530
+++ crash-4.0-3.7-rachita/netdump.c	2006-10-16 18:19:21.430947080 +0530
@@ -28,6 +28,7 @@ static void dump_Elf64_Phdr(Elf64_Phdr *
 static size_t dump_Elf64_Nhdr(Elf64_Off offset, int);
 static void get_netdump_regs_ppc64(struct bt_info *, ulong *, ulong *);
 static physaddr_t xen_kdump_p2m(physaddr_t);
+extern int has_unwind_info;
 
 #define ELFSTORE 1
 #define ELFREAD  0
@@ -771,8 +772,11 @@ netdump_memory_dump(FILE *fp)
 			dump_Elf64_Phdr(nd->load64 + i, ELFREAD);
         	offset64 = nd->notes64->p_offset;
         	for (tot = 0; tot < nd->notes64->p_filesz; tot += len) {
+		   if (has_unwind_info)
+                	len = dump_Elf64_Nhdr(offset64, ELFSTORE);
+		   else
                 	len = dump_Elf64_Nhdr(offset64, ELFREAD);
-                	offset64 += len;
+                   offset64 += len;
         	}
 		break;
 	}
@@ -1562,8 +1566,10 @@ get_netdump_regs_x86_64(struct bt_info *
         if (is_task_active(bt->task)) 
                 bt->flags |= BT_DUMPFILE_SEARCH;
 
-	if ((NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) &&
-            VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task)) {
+	if (((NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) &&
+   	      VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task)) ||
+	      (KDUMP_DUMPFILE() && has_unwind_info && (bt->flags &
+			BT_DUMPFILE_SEARCH))) {
 		if (nd->num_prstatus_notes > 1)
                 	note = (Elf64_Nhdr *)
 				nd->nt_prstatus_percpu[bt->tc->processor];
@@ -1574,9 +1580,21 @@ get_netdump_regs_x86_64(struct bt_info *
                 len = roundup(len + note->n_namesz, 4);
                 len = roundup(len + note->n_descsz, 4);
 
+		if KDUMP_DUMPFILE() {
+			ASSIGN_SIZE(user_regs_struct) = 27 * sizeof(unsigned long);
+			ASSIGN_OFFSET(user_regs_struct_rsp) = 19 * sizeof(unsigned long);
+			ASSIGN_OFFSET(user_regs_struct_rip) = 16 * sizeof(unsigned long);
+		}
                 user_regs = ((char *)note + len)
                         - SIZE(user_regs_struct) - sizeof(long);
 
+		if KDUMP_DUMPFILE() {
+			*rspp = *(ulong *)(user_regs + OFFSET(user_regs_struct_rsp));
+			*ripp = *(ulong *)(user_regs + OFFSET(user_regs_struct_rip));
+			if (*ripp && *rspp)
+				return;
+		}
+			
 		if (CRASHDEBUG(1)) {
                 	rsp = ULONG(user_regs + OFFSET(user_regs_struct_rsp));
                 	rip = ULONG(user_regs + OFFSET(user_regs_struct_rip));
diff -puN Makefile~crash-dwarf-unwind Makefile
--- crash-4.0-3.7/Makefile~crash-dwarf-unwind	2006-10-16 18:20:04.651376576 +0530
+++ crash-4.0-3.7-rachita/Makefile	2006-10-16 18:20:10.967416392 +0530
@@ -25,7 +25,7 @@ PROGRAM=crash
 # Supported targets: X86 ALPHA PPC IA64 PPC64
 # TARGET will be configured automatically by configure
 #
-TARGET=
+TARGET=X86_64
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 ifeq ($(ARCH), ppc64)
@@ -37,7 +37,7 @@ endif
 #
 GDB=gdb-6.1
 GDB_FILES=${GDB_6.1_FILES}
-GDB_OFILES=
+GDB_OFILES=${GDB_6.1_OFILES}
 
 GDB_PATCH_FILES=gdb-6.1.patch
 
@@ -69,7 +69,7 @@ LKCD_DUMP_HFILES=lkcd_vmdump_v1.h lkcd_v
         lkcd_dump_v7.h lkcd_dump_v8.h lkcd_fix_mem.h
 LKCD_TRACE_HFILES=lkcd_x86_trace.h
 IBM_HFILES=ibm_common.h
-UNWIND_HFILES=unwind.h unwind_i.h rse.h
+UNWIND_HFILES=unwind.h unwind_i.h rse.h unwind_x86_64.h
 
 CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \
 	kernel.c test.c gdb_interface.c configure.c net.c dev.c \
@@ -77,7 +77,7 @@ CFILES=main.c tools.c global_data.c memo
 	extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c \
 	lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c\
 	lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c \
-	netdump.c diskdump.c xendump.c unwind.c unwind_decoder.c
+	netdump.c diskdump.c xendump.c unwind.c unwind_decoder.c unwind_x86_64.c
 
 SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \
 	${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \
@@ -89,7 +89,7 @@ OBJECT_FILES=main.o tools.o global_data.
 	extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o \
 	lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \
 	lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o xendump.o \
-	lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o
+	lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o unwind_x86_64.o
 
 # These are the current set of crash extensions sources.  They are not built
 # by default unless the third command line of the "all:" stanza is uncommented.
@@ -387,6 +387,9 @@ extensions.o: ${GENERIC_HFILES} extensio
 lkcd_x86_trace.o: ${GENERIC_HFILES} ${LKCD_TRACE_HFILES} lkcd_x86_trace.c 
 	cc -c ${CFLAGS} -DREDHAT lkcd_x86_trace.c ${WARNING_OPTIONS} ${WARNING_ERROR}
 
+unwind_x86_64.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_x86_64.c
+	cc -c ${CFLAGS} -DREDHAT -DUNWIND_V1 unwind_x86_64.c -o unwind_x86_64.o ${WARNING_OPTIONS} ${WARNING_ERROR}
+
 unwind_v1.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c
 	cc -c ${CFLAGS} -DREDHAT -DUNWIND_V1 unwind.c -o unwind_v1.o ${WARNING_OPTIONS} ${WARNING_ERROR}
 
_




More information about the Crash-utility mailing list