Blame unwind_x86_32_64.c

Packit bf408e
/*
Packit bf408e
 * This program is free software; you can redistribute it and/or modify
Packit bf408e
 * it under the terms of the GNU General Public License as published by
Packit bf408e
 * the Free Software Foundation; either version 2 of the License, or
Packit bf408e
 * (at your option) any later version.
Packit bf408e
 *
Packit bf408e
 * This program is distributed in the hope that it will be useful,
Packit bf408e
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bf408e
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit bf408e
 * GNU General Public License for more details.
Packit bf408e
 */
Packit bf408e
Packit bf408e
#if defined(X86_64)
Packit bf408e
/*
Packit bf408e
 * Support for genarating DWARF CFI based backtraces.
Packit bf408e
 * Borrowed heavily from the kernel's implementation of unwinding using the
Packit bf408e
 * DWARF CFI written by Jan Beulich
Packit bf408e
 */
Packit bf408e
Packit bf408e
#ifdef X86_64
Packit bf408e
#include "unwind_x86_64.h"
Packit bf408e
#endif
Packit bf408e
#ifdef X86
Packit bf408e
#include "unwind_x86.h"
Packit bf408e
#endif
Packit bf408e
Packit bf408e
#include "defs.h"
Packit bf408e
Packit bf408e
#define MAX_STACK_DEPTH 8
Packit bf408e
Packit bf408e
static struct local_unwind_table {
Packit bf408e
        struct {
Packit bf408e
                unsigned long pc;
Packit bf408e
                unsigned long range;
Packit bf408e
        } core, init;
Packit bf408e
        void *address;
Packit bf408e
        unsigned long size;
Packit bf408e
} *local_unwind_tables, default_unwind_table;
Packit bf408e
Packit bf408e
static int gather_in_memory_unwind_tables(void);
Packit bf408e
static int populate_local_tables(ulong, char *);
Packit bf408e
static int unwind_tables_cnt = 0;
Packit bf408e
static struct local_unwind_table *find_table(unsigned long);
Packit bf408e
static void dump_local_unwind_tables(void);
Packit bf408e
Packit bf408e
static const struct {
Packit bf408e
	unsigned offs:BITS_PER_LONG / 2;
Packit bf408e
	unsigned width:BITS_PER_LONG / 2;
Packit bf408e
} reg_info[] = {
Packit bf408e
	UNW_REGISTER_INFO
Packit bf408e
};
Packit bf408e
Packit bf408e
#undef PTREGS_INFO
Packit bf408e
#undef EXTRA_INFO
Packit bf408e
Packit bf408e
#ifndef REG_INVALID
Packit bf408e
#define REG_INVALID(r) (reg_info[r].width == 0)
Packit bf408e
#endif
Packit bf408e
Packit bf408e
#define DW_CFA_nop                          0x00
Packit bf408e
#define DW_CFA_set_loc                      0x01
Packit bf408e
#define DW_CFA_advance_loc1                 0x02
Packit bf408e
#define DW_CFA_advance_loc2                 0x03
Packit bf408e
#define DW_CFA_advance_loc4                 0x04
Packit bf408e
#define DW_CFA_offset_extended              0x05
Packit bf408e
#define DW_CFA_restore_extended             0x06
Packit bf408e
#define DW_CFA_undefined                    0x07
Packit bf408e
#define DW_CFA_same_value                   0x08
Packit bf408e
#define DW_CFA_register                     0x09
Packit bf408e
#define DW_CFA_remember_state               0x0a
Packit bf408e
#define DW_CFA_restore_state                0x0b
Packit bf408e
#define DW_CFA_def_cfa                      0x0c
Packit bf408e
#define DW_CFA_def_cfa_register             0x0d
Packit bf408e
#define DW_CFA_def_cfa_offset               0x0e
Packit bf408e
#define DW_CFA_def_cfa_expression           0x0f
Packit bf408e
#define DW_CFA_expression                   0x10
Packit bf408e
#define DW_CFA_offset_extended_sf           0x11
Packit bf408e
#define DW_CFA_def_cfa_sf                   0x12
Packit bf408e
#define DW_CFA_def_cfa_offset_sf            0x13
Packit bf408e
#define DW_CFA_val_offset                   0x14
Packit bf408e
#define DW_CFA_val_offset_sf                0x15
Packit bf408e
#define DW_CFA_val_expression               0x16
Packit bf408e
#define DW_CFA_lo_user                      0x1c
Packit bf408e
#define DW_CFA_GNU_window_save              0x2d
Packit bf408e
#define DW_CFA_GNU_args_size                0x2e
Packit bf408e
#define DW_CFA_GNU_negative_offset_extended 0x2f
Packit bf408e
#define DW_CFA_hi_user                      0x3f
Packit bf408e
Packit bf408e
#define DW_EH_PE_FORM     0x07
Packit bf408e
#define DW_EH_PE_native   0x00
Packit bf408e
#define DW_EH_PE_leb128   0x01
Packit bf408e
#define DW_EH_PE_data2    0x02
Packit bf408e
#define DW_EH_PE_data4    0x03
Packit bf408e
#define DW_EH_PE_data8    0x04
Packit bf408e
#define DW_EH_PE_signed   0x08
Packit bf408e
#define DW_EH_PE_ADJUST   0x70
Packit bf408e
#define DW_EH_PE_abs      0x00
Packit bf408e
#define DW_EH_PE_pcrel    0x10
Packit bf408e
#define DW_EH_PE_textrel  0x20
Packit bf408e
#define DW_EH_PE_datarel  0x30
Packit bf408e
#define DW_EH_PE_funcrel  0x40
Packit bf408e
#define DW_EH_PE_aligned  0x50
Packit bf408e
#define DW_EH_PE_indirect 0x80
Packit bf408e
#define DW_EH_PE_omit     0xff
Packit bf408e
Packit bf408e
#define min(x,y) ({ \
Packit bf408e
        typeof(x) _x = (x);     \
Packit bf408e
        typeof(y) _y = (y);     \
Packit bf408e
        (void) (&_x == &_y);            \
Packit bf408e
        _x < _y ? _x : _y; })
Packit bf408e
Packit bf408e
#define max(x,y) ({ \
Packit bf408e
        typeof(x) _x = (x);     \
Packit bf408e
        typeof(y) _y = (y);     \
Packit bf408e
        (void) (&_x == &_y);            \
Packit bf408e
        _x > _y ? _x : _y; })
Packit bf408e
#define STACK_LIMIT(ptr)     (((ptr) - 1) & ~(THREAD_SIZE - 1))
Packit bf408e
Packit bf408e
typedef unsigned long uleb128_t;
Packit bf408e
typedef   signed long sleb128_t;
Packit bf408e
Packit bf408e
struct unwind_item {
Packit bf408e
	enum item_location {
Packit bf408e
		Nowhere,
Packit bf408e
		Memory,
Packit bf408e
		Register,
Packit bf408e
		Value
Packit bf408e
	} where;
Packit bf408e
	uleb128_t value;
Packit bf408e
};
Packit bf408e
Packit bf408e
struct unwind_state {
Packit bf408e
	uleb128_t loc, org;
Packit bf408e
	const u8 *cieStart, *cieEnd;
Packit bf408e
	uleb128_t codeAlign;
Packit bf408e
	sleb128_t dataAlign;
Packit bf408e
	struct cfa {
Packit bf408e
		uleb128_t reg, offs;
Packit bf408e
	} cfa;
Packit bf408e
	struct unwind_item regs[ARRAY_SIZE(reg_info)];
Packit bf408e
	unsigned stackDepth:8;
Packit bf408e
	unsigned version:8;
Packit bf408e
	const u8 *label;
Packit bf408e
	const u8 *stack[MAX_STACK_DEPTH];
Packit bf408e
};
Packit bf408e
Packit bf408e
static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
Packit bf408e
Packit bf408e
static uleb128_t get_uleb128(const u8 **pcur, const u8 *end)
Packit bf408e
{
Packit bf408e
	const u8 *cur = *pcur;
Packit bf408e
	uleb128_t value;
Packit bf408e
	unsigned shift;
Packit bf408e
Packit bf408e
	for (shift = 0, value = 0; cur < end; shift += 7) {
Packit bf408e
		if (shift + 7 > 8 * sizeof(value)
Packit bf408e
		    && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
Packit bf408e
			cur = end + 1;
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
		value |= (uleb128_t)(*cur & 0x7f) << shift;
Packit bf408e
		if (!(*cur++ & 0x80))
Packit bf408e
			break;
Packit bf408e
	}
Packit bf408e
	*pcur = cur;
Packit bf408e
Packit bf408e
	return value;
Packit bf408e
}
Packit bf408e
Packit bf408e
static sleb128_t get_sleb128(const u8 **pcur, const u8 *end)
Packit bf408e
{
Packit bf408e
	const u8 *cur = *pcur;
Packit bf408e
	sleb128_t value;
Packit bf408e
	unsigned shift;
Packit bf408e
Packit bf408e
	for (shift = 0, value = 0; cur < end; shift += 7) {
Packit bf408e
		if (shift + 7 > 8 * sizeof(value)
Packit bf408e
		    && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
Packit bf408e
			cur = end + 1;
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
		value |= (sleb128_t)(*cur & 0x7f) << shift;
Packit bf408e
		if (!(*cur & 0x80)) {
Packit bf408e
			value |= -(*cur++ & 0x40) << shift;
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	*pcur = cur;
Packit bf408e
Packit bf408e
	return value;
Packit bf408e
}
Packit bf408e
Packit bf408e
static unsigned long read_pointer(const u8 **pLoc,
Packit bf408e
                                  const void *end,
Packit bf408e
                                  signed ptrType)
Packit bf408e
{
Packit bf408e
	unsigned long value = 0;
Packit bf408e
	union {
Packit bf408e
		const u8 *p8;
Packit bf408e
		const u16 *p16u;
Packit bf408e
		const s16 *p16s;
Packit bf408e
		const u32 *p32u;
Packit bf408e
		const s32 *p32s;
Packit bf408e
		const unsigned long *pul;
Packit bf408e
	} ptr;
Packit bf408e
Packit bf408e
	if (ptrType < 0 || ptrType == DW_EH_PE_omit)
Packit bf408e
		return 0;
Packit bf408e
	ptr.p8 = *pLoc;
Packit bf408e
	switch(ptrType & DW_EH_PE_FORM) {
Packit bf408e
	case DW_EH_PE_data2:
Packit bf408e
		if (end < (const void *)(ptr.p16u + 1))
Packit bf408e
			return 0;
Packit bf408e
		if(ptrType & DW_EH_PE_signed)
Packit bf408e
			value = get_unaligned(ptr.p16s++);
Packit bf408e
		else
Packit bf408e
			value = get_unaligned(ptr.p16u++);
Packit bf408e
		break;
Packit bf408e
	case DW_EH_PE_data4:
Packit bf408e
#ifdef CONFIG_64BIT
Packit bf408e
		if (end < (const void *)(ptr.p32u + 1))
Packit bf408e
			return 0;
Packit bf408e
		if(ptrType & DW_EH_PE_signed)
Packit bf408e
			value = get_unaligned(ptr.p32s++);
Packit bf408e
		else
Packit bf408e
			value = get_unaligned(ptr.p32u++);
Packit bf408e
		break;
Packit bf408e
	case DW_EH_PE_data8:
Packit bf408e
		BUILD_BUG_ON(sizeof(u64) != sizeof(value));
Packit bf408e
#else
Packit bf408e
		BUILD_BUG_ON(sizeof(u32) != sizeof(value));
Packit bf408e
#endif
Packit bf408e
	case DW_EH_PE_native:
Packit bf408e
		if (end < (const void *)(ptr.pul + 1))
Packit bf408e
			return 0;
Packit bf408e
		value = get_unaligned(ptr.pul++);
Packit bf408e
		break;
Packit bf408e
	case DW_EH_PE_leb128:
Packit bf408e
		BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value));
Packit bf408e
		value = ptrType & DW_EH_PE_signed
Packit bf408e
		        ? get_sleb128(&ptr.p8, end)
Packit bf408e
		        : get_uleb128(&ptr.p8, end);
Packit bf408e
		if ((const void *)ptr.p8 > end)
Packit bf408e
			return 0;
Packit bf408e
		break;
Packit bf408e
	default:
Packit bf408e
		return 0;
Packit bf408e
	}
Packit bf408e
	switch(ptrType & DW_EH_PE_ADJUST) {
Packit bf408e
	case DW_EH_PE_abs:
Packit bf408e
		break;
Packit bf408e
	case DW_EH_PE_pcrel:
Packit bf408e
		value += (unsigned long)*pLoc;
Packit bf408e
		break;
Packit bf408e
	default:
Packit bf408e
		return 0;
Packit bf408e
	}
Packit bf408e
Packit bf408e
/*	TBD
Packit bf408e
	if ((ptrType & DW_EH_PE_indirect)
Packit bf408e
	    && __get_user(value, (unsigned long *)value))
Packit bf408e
		return 0;
Packit bf408e
*/
Packit bf408e
	*pLoc = ptr.p8;
Packit bf408e
Packit bf408e
	return value;
Packit bf408e
}
Packit bf408e
Packit bf408e
static signed fde_pointer_type(const u32 *cie)
Packit bf408e
{
Packit bf408e
	const u8 *ptr = (const u8 *)(cie + 2);
Packit bf408e
	unsigned version = *ptr;
Packit bf408e
Packit bf408e
	if (version != 1)
Packit bf408e
		return -1; /* unsupported */
Packit bf408e
	if (*++ptr) {
Packit bf408e
		const char *aug;
Packit bf408e
		const u8 *end = (const u8 *)(cie + 1) + *cie;
Packit bf408e
		uleb128_t len;
Packit bf408e
Packit bf408e
		/* check if augmentation size is first (and thus present) */
Packit bf408e
		if (*ptr != 'z')
Packit bf408e
			return -1;
Packit bf408e
		/* check if augmentation string is nul-terminated */
Packit bf408e
		if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL)
Packit bf408e
			return -1;
Packit bf408e
		++ptr; /* skip terminator */
Packit bf408e
		get_uleb128(&ptr, end); /* skip code alignment */
Packit bf408e
		get_sleb128(&ptr, end); /* skip data alignment */
Packit bf408e
		/* skip return address column */
Packit bf408e
		version <= 1 ? (void)++ptr : (void)get_uleb128(&ptr, end);
Packit bf408e
		len = get_uleb128(&ptr, end); /* augmentation length */
Packit bf408e
		if (ptr + len < ptr || ptr + len > end)
Packit bf408e
			return -1;
Packit bf408e
		end = ptr + len;
Packit bf408e
		while (*++aug) {
Packit bf408e
			if (ptr >= end)
Packit bf408e
				return -1;
Packit bf408e
			switch(*aug) {
Packit bf408e
			case 'L':
Packit bf408e
				++ptr;
Packit bf408e
				break;
Packit bf408e
			case 'P': {
Packit bf408e
					signed ptrType = *ptr++;
Packit bf408e
Packit bf408e
					if (!read_pointer(&ptr, end, ptrType) || 					     ptr > end)
Packit bf408e
						return -1;
Packit bf408e
				}
Packit bf408e
				break;
Packit bf408e
			case 'R':
Packit bf408e
				return *ptr;
Packit bf408e
			default:
Packit bf408e
				return -1;
Packit bf408e
			}
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	return DW_EH_PE_native|DW_EH_PE_abs;
Packit bf408e
}
Packit bf408e
Packit bf408e
static int advance_loc(unsigned long delta, struct unwind_state *state)
Packit bf408e
{
Packit bf408e
	state->loc += delta * state->codeAlign;
Packit bf408e
Packit bf408e
	return delta > 0;
Packit bf408e
}
Packit bf408e
Packit bf408e
static void set_rule(uleb128_t reg,
Packit bf408e
                     enum item_location where,
Packit bf408e
                     uleb128_t value,
Packit bf408e
                     struct unwind_state *state)
Packit bf408e
{
Packit bf408e
	if (reg < ARRAY_SIZE(state->regs)) {
Packit bf408e
		state->regs[reg].where = where;
Packit bf408e
		state->regs[reg].value = value;
Packit bf408e
	}
Packit bf408e
}
Packit bf408e
Packit bf408e
static int processCFI(const u8 *start,
Packit bf408e
                      const u8 *end,
Packit bf408e
                      unsigned long targetLoc,
Packit bf408e
                      signed ptrType,
Packit bf408e
                      struct unwind_state *state)
Packit bf408e
{
Packit bf408e
	union {
Packit bf408e
		const u8 *p8;
Packit bf408e
		const u16 *p16;
Packit bf408e
		const u32 *p32;
Packit bf408e
	} ptr;
Packit bf408e
	int result = 1;
Packit bf408e
Packit bf408e
	if (start != state->cieStart) {
Packit bf408e
		state->loc = state->org;
Packit bf408e
		result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, state);
Packit bf408e
		if (targetLoc == 0 && state->label == NULL)
Packit bf408e
			return result;
Packit bf408e
	}
Packit bf408e
	for (ptr.p8 = start; result && ptr.p8 < end; ) {
Packit bf408e
		switch(*ptr.p8 >> 6) {
Packit bf408e
			uleb128_t value;
Packit bf408e
Packit bf408e
		case 0:
Packit bf408e
			switch(*ptr.p8++) {
Packit bf408e
			case DW_CFA_nop:
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_set_loc:
Packit bf408e
				if ((state->loc = read_pointer(&ptr.p8, end,
Packit bf408e
								ptrType)) == 0)
Packit bf408e
					result = 0;
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_advance_loc1:
Packit bf408e
				result = ptr.p8 < end && advance_loc(*ptr.p8++, state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_advance_loc2:
Packit bf408e
				result = ptr.p8 <= end + 2
Packit bf408e
				         && advance_loc(*ptr.p16++, state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_advance_loc4:
Packit bf408e
				result = ptr.p8 <= end + 4
Packit bf408e
				         && advance_loc(*ptr.p32++, state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_offset_extended:
Packit bf408e
				value = get_uleb128(&ptr.p8, end);
Packit bf408e
				set_rule(value, Memory,
Packit bf408e
					get_uleb128(&ptr.p8, end), state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_val_offset:
Packit bf408e
				value = get_uleb128(&ptr.p8, end);
Packit bf408e
				set_rule(value, Value,
Packit bf408e
					get_uleb128(&ptr.p8, end), state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_offset_extended_sf:
Packit bf408e
				value = get_uleb128(&ptr.p8, end);
Packit bf408e
				set_rule(value, Memory,
Packit bf408e
					get_sleb128(&ptr.p8, end), state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_val_offset_sf:
Packit bf408e
				value = get_uleb128(&ptr.p8, end);
Packit bf408e
				set_rule(value, Value,
Packit bf408e
					get_sleb128(&ptr.p8, end), state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_restore_extended:
Packit bf408e
			case DW_CFA_undefined:
Packit bf408e
			case DW_CFA_same_value:
Packit bf408e
				set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0,	state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_register:
Packit bf408e
				value = get_uleb128(&ptr.p8, end);
Packit bf408e
				set_rule(value, Register,
Packit bf408e
				         get_uleb128(&ptr.p8, end), state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_remember_state:
Packit bf408e
				if (ptr.p8 == state->label) {
Packit bf408e
					state->label = NULL;
Packit bf408e
					return 1;
Packit bf408e
				}
Packit bf408e
				if (state->stackDepth >= MAX_STACK_DEPTH)
Packit bf408e
					return 0;
Packit bf408e
				state->stack[state->stackDepth++] = ptr.p8;
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_restore_state:
Packit bf408e
				if (state->stackDepth) {
Packit bf408e
					const uleb128_t loc = state->loc;
Packit bf408e
					const u8 *label = state->label;
Packit bf408e
Packit bf408e
					state->label = state->stack[state->stackDepth - 1];
Packit bf408e
					memcpy(&state->cfa, &badCFA, sizeof(state->cfa));
Packit bf408e
					memset(state->regs, 0, sizeof(state->regs));
Packit bf408e
					state->stackDepth = 0;
Packit bf408e
					result = processCFI(start, end, 0, ptrType, state);
Packit bf408e
					state->loc = loc;
Packit bf408e
					state->label = label;
Packit bf408e
				} else
Packit bf408e
					return 0;
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_def_cfa:
Packit bf408e
				state->cfa.reg = get_uleb128(&ptr.p8, end);
Packit bf408e
				/*nobreak*/
Packit bf408e
			case DW_CFA_def_cfa_offset:
Packit bf408e
				state->cfa.offs = get_uleb128(&ptr.p8, end);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_def_cfa_sf:
Packit bf408e
				state->cfa.reg = get_uleb128(&ptr.p8, end);
Packit bf408e
				/*nobreak*/
Packit bf408e
			case DW_CFA_def_cfa_offset_sf:
Packit bf408e
				state->cfa.offs = get_sleb128(&ptr.p8, end)
Packit bf408e
				                  * state->dataAlign;
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_def_cfa_register:
Packit bf408e
				state->cfa.reg = get_uleb128(&ptr.p8, end);
Packit bf408e
				break;
Packit bf408e
			/*todo case DW_CFA_def_cfa_expression: */
Packit bf408e
			/*todo case DW_CFA_expression: */
Packit bf408e
			/*todo case DW_CFA_val_expression: */
Packit bf408e
			case DW_CFA_GNU_args_size:
Packit bf408e
				get_uleb128(&ptr.p8, end);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_GNU_negative_offset_extended:
Packit bf408e
				value = get_uleb128(&ptr.p8, end);
Packit bf408e
				set_rule(value, Memory, (uleb128_t)0 -
Packit bf408e
				         get_uleb128(&ptr.p8, end), state);
Packit bf408e
				break;
Packit bf408e
			case DW_CFA_GNU_window_save:
Packit bf408e
			default:
Packit bf408e
				result = 0;
Packit bf408e
				break;
Packit bf408e
			}
Packit bf408e
			break;
Packit bf408e
		case 1:
Packit bf408e
			result = advance_loc(*ptr.p8++ & 0x3f, state);
Packit bf408e
			break;
Packit bf408e
		case 2:
Packit bf408e
			value = *ptr.p8++ & 0x3f;
Packit bf408e
			set_rule(value, Memory, get_uleb128(&ptr.p8, end),
Packit bf408e
				 state);
Packit bf408e
			break;
Packit bf408e
		case 3:
Packit bf408e
			set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
		if (ptr.p8 > end)
Packit bf408e
			result = 0;
Packit bf408e
		if (result && targetLoc != 0 && targetLoc < state->loc)
Packit bf408e
			return 1;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	return result
Packit bf408e
	   && ptr.p8 == end
Packit bf408e
	   && (targetLoc == 0
Packit bf408e
	    || (/*todo While in theory this should apply, gcc in practice omits
Packit bf408e
	          everything past the function prolog, and hence the location
Packit bf408e
	          never reaches the end of the function.
Packit bf408e
	        targetLoc < state->loc &&*/ state->label == NULL));
Packit bf408e
}
Packit bf408e
Packit bf408e
Packit bf408e
/* Unwind to previous to frame.  Returns 0 if successful, negative
Packit bf408e
 * number in case of an error. */
Packit bf408e
int 
Packit bf408e
unwind(struct unwind_frame_info *frame, int is_ehframe)
Packit bf408e
{
Packit bf408e
#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
Packit bf408e
	const u32 *fde = NULL, *cie = NULL;
Packit bf408e
	const u8 *ptr = NULL, *end = NULL;
Packit bf408e
	unsigned long startLoc = 0, endLoc = 0, cfa;
Packit bf408e
	unsigned i;
Packit bf408e
	signed ptrType = -1;
Packit bf408e
	uleb128_t retAddrReg = 0;
Packit bf408e
//	struct unwind_table *table;
Packit bf408e
	void *unwind_table;
Packit bf408e
	struct local_unwind_table *table;
Packit bf408e
	struct unwind_state state;
Packit bf408e
	u64 reg_ptr = 0;
Packit bf408e
Packit bf408e
Packit bf408e
	if (UNW_PC(frame) == 0)
Packit bf408e
		return -EINVAL;
Packit bf408e
Packit bf408e
	if ((table = find_table(UNW_PC(frame)))) {
Packit bf408e
//		unsigned long tableSize = unwind_table_size;
Packit bf408e
		unsigned long tableSize = table->size;
Packit bf408e
Packit bf408e
		unwind_table = table->address;
Packit bf408e
Packit bf408e
		for (fde = unwind_table;
Packit bf408e
		     tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde;
Packit bf408e
		     tableSize -= sizeof(*fde) + *fde,
Packit bf408e
		     fde += 1 + *fde / sizeof(*fde)) {
Packit bf408e
			if (!*fde || (*fde & (sizeof(*fde) - 1)))
Packit bf408e
				break;
Packit bf408e
			if (is_ehframe && !fde[1])
Packit bf408e
				continue; /* this is a CIE */
Packit bf408e
			else if (fde[1] == 0xffffffff)
Packit bf408e
				continue; /* this is a CIE */
Packit bf408e
			if ((fde[1] & (sizeof(*fde) - 1))
Packit bf408e
			    || fde[1] > (unsigned long)(fde + 1)
Packit bf408e
			                - (unsigned long)unwind_table)
Packit bf408e
				continue; /* this is not a valid FDE */
Packit bf408e
			if (is_ehframe)
Packit bf408e
				cie = fde + 1 - fde[1] / sizeof(*fde);
Packit bf408e
			else
Packit bf408e
				cie = unwind_table + fde[1];
Packit bf408e
			if (*cie <= sizeof(*cie) + 4
Packit bf408e
			    || *cie >= fde[1] - sizeof(*fde)
Packit bf408e
			    || (*cie & (sizeof(*cie) - 1))
Packit bf408e
			    || (cie[1] != 0xffffffff && cie[1])
Packit bf408e
			    || (ptrType = fde_pointer_type(cie)) < 0) {
Packit bf408e
				cie = NULL; /* this is not a (valid) CIE */
Packit bf408e
				continue;
Packit bf408e
			}
Packit bf408e
			ptr = (const u8 *)(fde + 2);
Packit bf408e
			startLoc = read_pointer(&ptr,
Packit bf408e
			                        (const u8 *)(fde + 1) + *fde,
Packit bf408e
			                        ptrType);
Packit bf408e
			endLoc = startLoc
Packit bf408e
			         + read_pointer(&ptr,
Packit bf408e
			                        (const u8 *)(fde + 1) + *fde,
Packit bf408e
			                        ptrType & DW_EH_PE_indirect
Packit bf408e
			                        ? ptrType
Packit bf408e
			                        : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed));
Packit bf408e
			if (UNW_PC(frame) >= startLoc && UNW_PC(frame) < endLoc)
Packit bf408e
				break;
Packit bf408e
			cie = NULL;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	if (cie != NULL) {
Packit bf408e
		memset(&state, 0, sizeof(state));
Packit bf408e
		state.cieEnd = ptr; /* keep here temporarily */
Packit bf408e
		ptr = (const u8 *)(cie + 2);
Packit bf408e
		end = (const u8 *)(cie + 1) + *cie;
Packit bf408e
		if ((state.version = *ptr) != 1)
Packit bf408e
			cie = NULL; /* unsupported version */
Packit bf408e
		else if (*++ptr) {
Packit bf408e
			/* check if augmentation size is first (and thus present) */
Packit bf408e
			if (*ptr == 'z') {
Packit bf408e
				/* check for ignorable (or already handled)
Packit bf408e
				 * nul-terminated augmentation string */
Packit bf408e
				while (++ptr < end && *ptr)
Packit bf408e
					if (strchr("LPR", *ptr) == NULL)
Packit bf408e
						break;
Packit bf408e
			}
Packit bf408e
			if (ptr >= end || *ptr)
Packit bf408e
				cie = NULL;
Packit bf408e
		}
Packit bf408e
		++ptr;
Packit bf408e
	}
Packit bf408e
	if (cie != NULL) {
Packit bf408e
		/* get code aligment factor */
Packit bf408e
		state.codeAlign = get_uleb128(&ptr, end);
Packit bf408e
		/* get data aligment factor */
Packit bf408e
		state.dataAlign = get_sleb128(&ptr, end);
Packit bf408e
		if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
Packit bf408e
			cie = NULL;
Packit bf408e
		else {
Packit bf408e
			retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end);
Packit bf408e
			/* skip augmentation */
Packit bf408e
			if (((const char *)(cie + 2))[1] == 'z')
Packit bf408e
				ptr += get_uleb128(&ptr, end);
Packit bf408e
			if (ptr > end
Packit bf408e
			   || retAddrReg >= ARRAY_SIZE(reg_info)
Packit bf408e
			   || REG_INVALID(retAddrReg)
Packit bf408e
			   || reg_info[retAddrReg].width != sizeof(unsigned long))
Packit bf408e
				cie = NULL;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	if (cie != NULL) {
Packit bf408e
		state.cieStart = ptr;
Packit bf408e
		ptr = state.cieEnd;
Packit bf408e
		state.cieEnd = end;
Packit bf408e
		end = (const u8 *)(fde + 1) + *fde;
Packit bf408e
		/* skip augmentation */
Packit bf408e
		if (((const char *)(cie + 2))[1] == 'z') {
Packit bf408e
			uleb128_t augSize = get_uleb128(&ptr, end);
Packit bf408e
Packit bf408e
			if ((ptr += augSize) > end)
Packit bf408e
				fde = NULL;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	if (cie == NULL || fde == NULL)
Packit bf408e
		return -ENXIO;
Packit bf408e
Packit bf408e
	state.org = startLoc;
Packit bf408e
	memcpy(&state.cfa, &badCFA, sizeof(state.cfa));
Packit bf408e
	/* process instructions */
Packit bf408e
	if (!processCFI(ptr, end, UNW_PC(frame), ptrType, &state)
Packit bf408e
	   || state.loc > endLoc
Packit bf408e
	   || state.regs[retAddrReg].where == Nowhere
Packit bf408e
	   || state.cfa.reg >= ARRAY_SIZE(reg_info)
Packit bf408e
	   || reg_info[state.cfa.reg].width != sizeof(unsigned long)
Packit bf408e
	   || state.cfa.offs % sizeof(unsigned long)) {
Packit bf408e
		return -EIO;
Packit bf408e
		}
Packit bf408e
	/* update frame */
Packit bf408e
	cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
Packit bf408e
	startLoc = min((unsigned long)UNW_SP(frame), cfa);
Packit bf408e
	endLoc = max((unsigned long)UNW_SP(frame), cfa);
Packit bf408e
	if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) {
Packit bf408e
		startLoc = min(STACK_LIMIT(cfa), cfa);
Packit bf408e
		endLoc = max(STACK_LIMIT(cfa), cfa);
Packit bf408e
	}
Packit bf408e
#ifndef CONFIG_64BIT
Packit bf408e
# define CASES CASE(8); CASE(16); CASE(32)
Packit bf408e
#else
Packit bf408e
# define CASES CASE(8); CASE(16); CASE(32); CASE(64)
Packit bf408e
#endif
Packit bf408e
	for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
Packit bf408e
		if (REG_INVALID(i)) {
Packit bf408e
			if (state.regs[i].where == Nowhere)
Packit bf408e
				continue;
Packit bf408e
			return -EIO;
Packit bf408e
		}
Packit bf408e
		switch(state.regs[i].where) {
Packit bf408e
		default:
Packit bf408e
			break;
Packit bf408e
		case Register:
Packit bf408e
			if (state.regs[i].value >= ARRAY_SIZE(reg_info)
Packit bf408e
			   || REG_INVALID(state.regs[i].value)
Packit bf408e
			   || reg_info[i].width > reg_info[state.regs[i].value].width){
Packit bf408e
				return -EIO;
Packit bf408e
	}
Packit bf408e
			switch(reg_info[state.regs[i].value].width) {
Packit bf408e
#define CASE(n) \
Packit bf408e
			case sizeof(u##n): \
Packit bf408e
				state.regs[i].value = FRAME_REG(state.regs[i].value, \
Packit bf408e
				                                const u##n); \
Packit bf408e
				break
Packit bf408e
			CASES;
Packit bf408e
#undef CASE
Packit bf408e
			default:
Packit bf408e
				return -EIO;
Packit bf408e
			}
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
Packit bf408e
		if (REG_INVALID(i))
Packit bf408e
			continue;
Packit bf408e
		switch(state.regs[i].where) {
Packit bf408e
		case Nowhere:
Packit bf408e
			if (reg_info[i].width != sizeof(UNW_SP(frame))
Packit bf408e
			   || &FRAME_REG(i, __typeof__(UNW_SP(frame)))
Packit bf408e
			      != &UNW_SP(frame))
Packit bf408e
				continue;
Packit bf408e
			UNW_SP(frame) = cfa;
Packit bf408e
			break;
Packit bf408e
		case Register:
Packit bf408e
			switch(reg_info[i].width) {
Packit bf408e
#define CASE(n) case sizeof(u##n): \
Packit bf408e
				FRAME_REG(i, u##n) = state.regs[i].value; \
Packit bf408e
				break
Packit bf408e
			CASES;
Packit bf408e
#undef CASE
Packit bf408e
			default:
Packit bf408e
				return -EIO;
Packit bf408e
			}
Packit bf408e
			break;
Packit bf408e
		case Value:
Packit bf408e
			if (reg_info[i].width != sizeof(unsigned long)){
Packit bf408e
				return -EIO;}
Packit bf408e
			FRAME_REG(i, unsigned long) = cfa + state.regs[i].value
Packit bf408e
			                                    * state.dataAlign;
Packit bf408e
			break;
Packit bf408e
		case Memory: {
Packit bf408e
				unsigned long addr = cfa + state.regs[i].value
Packit bf408e
				                           * state.dataAlign;
Packit bf408e
				if ((state.regs[i].value * state.dataAlign)
Packit bf408e
				    % sizeof(unsigned long)
Packit bf408e
				    || addr < startLoc
Packit bf408e
				    || addr + sizeof(unsigned long) < addr
Packit bf408e
				    || addr + sizeof(unsigned long) > endLoc){
Packit bf408e
					return -EIO;}
Packit bf408e
				switch(reg_info[i].width) {
Packit bf408e
#define CASE(n)     case sizeof(u##n): \
Packit bf408e
					readmem(addr, KVADDR, &reg_ptr,sizeof(u##n), "register", RETURN_ON_ERROR|QUIET); \
Packit bf408e
					FRAME_REG(i, u##n) = (u##n)reg_ptr;\
Packit bf408e
					break
Packit bf408e
				CASES;
Packit bf408e
#undef CASE
Packit bf408e
				default:
Packit bf408e
					return -EIO;
Packit bf408e
				}
Packit bf408e
			}
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
	return 0;
Packit bf408e
#undef CASES
Packit bf408e
#undef FRAME_REG
Packit bf408e
}
Packit bf408e
Packit bf408e
/*
Packit bf408e
 *  Initialize the unwind table(s) in the best-case order:
Packit bf408e
 *
Packit bf408e
 *   1. Use the in-memory kernel and module unwind tables.
Packit bf408e
 *   2. Use the in-memory kernel-only .eh_frame data. (possible?)
Packit bf408e
 *   3. Use the kernel-only .eh_frame data from the vmlinux file.
Packit bf408e
 */ 
Packit bf408e
void 
Packit bf408e
init_unwind_table(void)
Packit bf408e
{
Packit bf408e
	ulong unwind_table_size;
Packit bf408e
	void *unwind_table;
Packit bf408e
Packit bf408e
	kt->flags &= ~DWARF_UNWIND;
Packit bf408e
Packit bf408e
	if (gather_in_memory_unwind_tables()) {
Packit bf408e
                if (CRASHDEBUG(1))
Packit bf408e
                        fprintf(fp, "init_unwind_table: DWARF_UNWIND_MEMORY (%d tables)\n",
Packit bf408e
				unwind_tables_cnt);
Packit bf408e
Packit bf408e
                kt->flags |= DWARF_UNWIND_MEMORY;
Packit bf408e
		if (unwind_tables_cnt > 1)
Packit bf408e
                	kt->flags |= DWARF_UNWIND_MODULES;
Packit bf408e
                if (!(kt->flags & NO_DWARF_UNWIND))
Packit bf408e
                        kt->flags |= DWARF_UNWIND;
Packit bf408e
Packit bf408e
		return;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	if (symbol_exists("__start_unwind") &&
Packit bf408e
	    symbol_exists("__end_unwind")) {
Packit bf408e
		unwind_table_size = symbol_value("__end_unwind") - 
Packit bf408e
			symbol_value("__start_unwind");
Packit bf408e
Packit bf408e
		if (!(unwind_table = malloc(unwind_table_size))) {
Packit bf408e
			error(WARNING, "cannot malloc unwind table space\n");
Packit bf408e
			goto try_eh_frame;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		if (!readmem(symbol_value("__start_unwind"), KVADDR, unwind_table,
Packit bf408e
            	    unwind_table_size, "unwind table", RETURN_ON_ERROR)) {
Packit bf408e
			error(WARNING, "cannot read unwind table data\n");
Packit bf408e
			free(unwind_table);
Packit bf408e
			goto try_eh_frame;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		kt->flags |= DWARF_UNWIND_MEMORY;
Packit bf408e
		if (!(kt->flags & NO_DWARF_UNWIND))
Packit bf408e
			kt->flags |= DWARF_UNWIND;
Packit bf408e
Packit bf408e
		default_unwind_table.size = unwind_table_size;
Packit bf408e
		default_unwind_table.address = unwind_table;
Packit bf408e
Packit bf408e
		if (CRASHDEBUG(1)) 
Packit bf408e
			fprintf(fp, "init_unwind_table: DWARF_UNWIND_MEMORY\n");
Packit bf408e
Packit bf408e
		return;
Packit bf408e
	}
Packit bf408e
Packit bf408e
try_eh_frame:
Packit bf408e
Packit bf408e
	if (st->dwarf_eh_frame_size || st->dwarf_debug_frame_size) {
Packit bf408e
		int fd;
Packit bf408e
		int is_ehframe = (!st->dwarf_debug_frame_size &&
Packit bf408e
				   st->dwarf_eh_frame_size);
Packit bf408e
Packit bf408e
		unwind_table_size = is_ehframe ? st->dwarf_eh_frame_size :
Packit bf408e
						 st->dwarf_debug_frame_size;
Packit bf408e
Packit bf408e
		if (!(unwind_table = malloc(unwind_table_size))) {
Packit bf408e
			error(WARNING, "cannot malloc unwind table space\n");
Packit bf408e
			return;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		if ((fd = open(pc->namelist, O_RDONLY)) < 0) {
Packit bf408e
			error(WARNING, "cannot open %s for %s data\n",
Packit bf408e
				pc->namelist, is_ehframe ? ".eh_frame" : ".debug_frame");
Packit bf408e
			free(unwind_table);
Packit bf408e
			return;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		if (is_ehframe)
Packit bf408e
			lseek(fd, st->dwarf_eh_frame_file_offset, SEEK_SET);
Packit bf408e
		else
Packit bf408e
			lseek(fd, st->dwarf_debug_frame_file_offset, SEEK_SET);
Packit bf408e
Packit bf408e
		if (read(fd, unwind_table, unwind_table_size) !=
Packit bf408e
		    unwind_table_size) {
Packit bf408e
			if (CRASHDEBUG(1))
Packit bf408e
				error(WARNING, "cannot read %s data from %s\n",
Packit bf408e
			        	is_ehframe ? ".eh_frame" : ".debug_frame", pc->namelist);
Packit bf408e
			free(unwind_table);
Packit bf408e
			close(fd);
Packit bf408e
			return;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		close(fd);
Packit bf408e
Packit bf408e
		default_unwind_table.size = unwind_table_size;
Packit bf408e
		default_unwind_table.address = unwind_table;
Packit bf408e
Packit bf408e
		kt->flags |= DWARF_UNWIND_EH_FRAME;
Packit bf408e
		if (!(kt->flags & NO_DWARF_UNWIND))
Packit bf408e
			kt->flags |= DWARF_UNWIND;
Packit bf408e
Packit bf408e
		if (CRASHDEBUG(1)) 
Packit bf408e
			fprintf(fp, "init_unwind_table: DWARF_UNWIND_EH_FRAME\n");
Packit bf408e
Packit bf408e
		return;
Packit bf408e
	}
Packit bf408e
}
Packit bf408e
Packit bf408e
/*
Packit bf408e
 *  Find the appropriate kernel-only "root_table" unwind_table,
Packit bf408e
 *  and pass it to populate_local_tables() to do the heavy lifting.
Packit bf408e
 */
Packit bf408e
static int 
Packit bf408e
gather_in_memory_unwind_tables(void)
Packit bf408e
{
Packit bf408e
	int i, cnt, found;
Packit bf408e
	struct syment *sp, *root_tables[10];
Packit bf408e
	char *root_table_buf;
Packit bf408e
	char buf[BUFSIZE];
Packit bf408e
	ulong name;
Packit bf408e
Packit bf408e
	STRUCT_SIZE_INIT(unwind_table, "unwind_table");
Packit bf408e
	MEMBER_OFFSET_INIT(unwind_table_core, "unwind_table", "core");
Packit bf408e
	MEMBER_OFFSET_INIT(unwind_table_init, "unwind_table", "init");
Packit bf408e
	MEMBER_OFFSET_INIT(unwind_table_address, "unwind_table", "address");
Packit bf408e
	MEMBER_OFFSET_INIT(unwind_table_size, "unwind_table", "size");
Packit bf408e
	MEMBER_OFFSET_INIT(unwind_table_link, "unwind_table", "link");
Packit bf408e
	MEMBER_OFFSET_INIT(unwind_table_name, "unwind_table", "name");
Packit bf408e
Packit bf408e
	if (INVALID_SIZE(unwind_table) ||
Packit bf408e
	    INVALID_MEMBER(unwind_table_core) ||
Packit bf408e
	    INVALID_MEMBER(unwind_table_init) ||
Packit bf408e
	    INVALID_MEMBER(unwind_table_address) ||
Packit bf408e
	    INVALID_MEMBER(unwind_table_size) ||
Packit bf408e
	    INVALID_MEMBER(unwind_table_link) ||
Packit bf408e
	    INVALID_MEMBER(unwind_table_name)) {
Packit bf408e
		if (CRASHDEBUG(1)) 
Packit bf408e
			error(NOTE, 
Packit bf408e
	    "unwind_table structure has changed, or does not exist in this kernel\n");
Packit bf408e
		return 0;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	/*
Packit bf408e
	 *  Unfortunately there are two kernel root_table symbols.
Packit bf408e
	 */
Packit bf408e
	if (!(cnt = get_syment_array("root_table", root_tables, 10)))
Packit bf408e
		return 0;
Packit bf408e
Packit bf408e
	root_table_buf = GETBUF(SIZE(unwind_table));
Packit bf408e
	for (i = found = 0; i < cnt; i++) {
Packit bf408e
		sp = root_tables[i];
Packit bf408e
		if (!readmem(sp->value, KVADDR, root_table_buf,
Packit bf408e
                    SIZE(unwind_table), "root unwind_table", 
Packit bf408e
		    RETURN_ON_ERROR|QUIET))
Packit bf408e
			goto gather_failed;
Packit bf408e
Packit bf408e
		name = ULONG(root_table_buf + OFFSET(unwind_table_name));
Packit bf408e
		if (read_string(name, buf, strlen("kernel")+1) && 
Packit bf408e
		    STREQ("kernel", buf)) {
Packit bf408e
			found++;
Packit bf408e
			if (CRASHDEBUG(1))
Packit bf408e
				fprintf(fp, "root_table name: %lx [%s]\n", 
Packit bf408e
					name, buf);
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
Packit bf408e
	if (!found)
Packit bf408e
		goto gather_failed;
Packit bf408e
Packit bf408e
	cnt = populate_local_tables(sp->value, root_table_buf);
Packit bf408e
Packit bf408e
	FREEBUF(root_table_buf);
Packit bf408e
	return cnt;
Packit bf408e
Packit bf408e
gather_failed:
Packit bf408e
Packit bf408e
	FREEBUF(root_table_buf);
Packit bf408e
	return 0;
Packit bf408e
}
Packit bf408e
Packit bf408e
/*
Packit bf408e
 *  Transfer the relevant data from the kernel and module unwind_table
Packit bf408e
 *  structures to the local_unwind_table structures.
Packit bf408e
 */
Packit bf408e
static int
Packit bf408e
populate_local_tables(ulong root, char *buf)
Packit bf408e
{
Packit bf408e
	struct list_data list_data, *ld;
Packit bf408e
	int i, cnt;
Packit bf408e
	ulong *table_list;
Packit bf408e
	ulong vaddr;
Packit bf408e
	struct local_unwind_table *tp;
Packit bf408e
Packit bf408e
        ld = &list_data;
Packit bf408e
        BZERO(ld, sizeof(struct list_data));
Packit bf408e
        ld->start = root;
Packit bf408e
        ld->member_offset = OFFSET(unwind_table_link);
Packit bf408e
	ld->flags = RETURN_ON_LIST_ERROR;
Packit bf408e
	if (CRASHDEBUG(1))
Packit bf408e
        	ld->flags |= VERBOSE;
Packit bf408e
Packit bf408e
	hq_open();
Packit bf408e
        cnt = do_list(ld);
Packit bf408e
	if (cnt == -1) {
Packit bf408e
		error(WARNING, "UNWIND: failed to gather unwind_table list");
Packit bf408e
		return 0;
Packit bf408e
	}
Packit bf408e
        table_list = (ulong *)GETBUF(cnt * sizeof(ulong));
Packit bf408e
	cnt = retrieve_list(table_list, cnt);
Packit bf408e
	hq_close();
Packit bf408e
Packit bf408e
	if (!(local_unwind_tables = 
Packit bf408e
	    malloc(sizeof(struct local_unwind_table) * cnt))) {
Packit bf408e
		error(WARNING, "cannot malloc unwind_table space (%d tables)\n",
Packit bf408e
			cnt);
Packit bf408e
		FREEBUF(table_list);
Packit bf408e
		return 0;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	for (i = 0; i < cnt; i++, tp++) {
Packit bf408e
Packit bf408e
                if (!readmem(table_list[i], KVADDR, buf,
Packit bf408e
                    SIZE(unwind_table), "unwind_table",
Packit bf408e
                    RETURN_ON_ERROR|QUIET)) {
Packit bf408e
			error(WARNING, "cannot read unwind_table\n");
Packit bf408e
			goto failed;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		tp = &local_unwind_tables[i];
Packit bf408e
Packit bf408e
		/*
Packit bf408e
		 *  Copy the required table info for find_table().
Packit bf408e
		 */
Packit bf408e
        	BCOPY(buf + OFFSET(unwind_table_core),
Packit bf408e
                	(char *)&tp->core.pc, sizeof(ulong)*2);
Packit bf408e
        	BCOPY(buf + OFFSET(unwind_table_init),
Packit bf408e
                	(char *)&tp->init.pc, sizeof(ulong)*2);
Packit bf408e
        	BCOPY(buf + OFFSET(unwind_table_size),
Packit bf408e
                	(char *)&tp->size, sizeof(ulong));
Packit bf408e
Packit bf408e
		/*
Packit bf408e
		 *  Then read the DWARF CFI data.
Packit bf408e
		 */
Packit bf408e
		vaddr = ULONG(buf + OFFSET(unwind_table_address));
Packit bf408e
Packit bf408e
		if (!(tp->address = malloc(tp->size))) {
Packit bf408e
			error(WARNING, "cannot malloc unwind_table space\n");
Packit bf408e
			goto failed;
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
                if (!readmem(vaddr, KVADDR, tp->address,
Packit bf408e
                    tp->size, "DWARF CFI data", RETURN_ON_ERROR|QUIET)) {
Packit bf408e
			error(WARNING, "cannot read unwind_table data\n");
Packit bf408e
			goto failed;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
Packit bf408e
	unwind_tables_cnt = cnt;
Packit bf408e
Packit bf408e
	if (CRASHDEBUG(7))
Packit bf408e
		dump_local_unwind_tables();
Packit bf408e
Packit bf408e
failed:
Packit bf408e
Packit bf408e
	FREEBUF(table_list);
Packit bf408e
	return unwind_tables_cnt;
Packit bf408e
}
Packit bf408e
Packit bf408e
/*
Packit bf408e
 *  Find the unwind_table containing a pc.
Packit bf408e
 */
Packit bf408e
static struct local_unwind_table *
Packit bf408e
find_table(unsigned long pc)
Packit bf408e
{
Packit bf408e
	int i;
Packit bf408e
	struct local_unwind_table *tp, *table;
Packit bf408e
Packit bf408e
	table = &default_unwind_table;
Packit bf408e
Packit bf408e
        for (i = 0; i < unwind_tables_cnt; i++, tp++) {
Packit bf408e
		tp = &local_unwind_tables[i];
Packit bf408e
                if ((pc >= tp->core.pc
Packit bf408e
                    && pc < tp->core.pc + tp->core.range)
Packit bf408e
                    || (pc >= tp->init.pc
Packit bf408e
                    && pc < tp->init.pc + tp->init.range)) {
Packit bf408e
			table = tp;
Packit bf408e
                        break;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
Packit bf408e
        return table;
Packit bf408e
}
Packit bf408e
Packit bf408e
static void 
Packit bf408e
dump_local_unwind_tables(void)
Packit bf408e
{
Packit bf408e
	int i, others; 
Packit bf408e
	struct local_unwind_table *tp;
Packit bf408e
Packit bf408e
	others = 0;
Packit bf408e
	fprintf(fp, "DWARF flags: (");
Packit bf408e
        if (kt->flags & DWARF_UNWIND)
Packit bf408e
                fprintf(fp, "%sDWARF_UNWIND", others++ ? "|" : "");
Packit bf408e
        if (kt->flags & NO_DWARF_UNWIND)
Packit bf408e
                fprintf(fp, "%sNO_DWARF_UNWIND", others++ ? "|" : "");
Packit bf408e
        if (kt->flags & DWARF_UNWIND_MEMORY)
Packit bf408e
                fprintf(fp, "%sDWARF_UNWIND_MEMORY", others++ ? "|" : "");
Packit bf408e
        if (kt->flags & DWARF_UNWIND_EH_FRAME)
Packit bf408e
                fprintf(fp, "%sDWARF_UNWIND_EH_FRAME", others++ ? "|" : "");
Packit bf408e
        if (kt->flags & DWARF_UNWIND_MODULES)
Packit bf408e
                fprintf(fp, "%sDWARF_UNWIND_MODULES", others++ ? "|" : "");
Packit bf408e
	fprintf(fp, ")\n\n");
Packit bf408e
Packit bf408e
	fprintf(fp, "default_unwind_table:\n");
Packit bf408e
	fprintf(fp, "      address: %lx\n",
Packit bf408e
		(ulong)default_unwind_table.address);
Packit bf408e
	fprintf(fp, "         size: %ld\n\n",
Packit bf408e
		(ulong)default_unwind_table.size);
Packit bf408e
Packit bf408e
	fprintf(fp, "local_unwind_tables[%d]:\n", unwind_tables_cnt);
Packit bf408e
        for (i = 0; i < unwind_tables_cnt; i++, tp++) {
Packit bf408e
		tp = &local_unwind_tables[i];
Packit bf408e
		fprintf(fp, "[%d]\n", i);
Packit bf408e
		fprintf(fp, "         core: pc: %lx\n", tp->core.pc);
Packit bf408e
		fprintf(fp, "        range: %ld\n", tp->core.range);
Packit bf408e
		fprintf(fp, "     init: pc: %lx\n", tp->init.pc);
Packit bf408e
		fprintf(fp, "        range: %ld\n", tp->init.range);
Packit bf408e
		fprintf(fp, "      address: %lx\n", (ulong)tp->address);
Packit bf408e
		fprintf(fp, "         size: %ld\n", tp->size);
Packit bf408e
	}
Packit bf408e
}
Packit bf408e
Packit bf408e
Packit bf408e
int 
Packit bf408e
dwarf_backtrace(struct bt_info *bt, int level, ulong stacktop)
Packit bf408e
{
Packit bf408e
	unsigned long bp, offset;
Packit bf408e
	struct syment *sp;
Packit bf408e
	char *name;
Packit bf408e
	struct unwind_frame_info *frame;
Packit bf408e
	int is_ehframe = (!st->dwarf_debug_frame_size && st->dwarf_eh_frame_size);
Packit bf408e
Packit bf408e
	frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info));
Packit bf408e
//	frame->regs.rsp = bt->stkptr;
Packit bf408e
//	frame->regs.rip = bt->instptr;
Packit bf408e
	UNW_SP(frame) = bt->stkptr;
Packit bf408e
	UNW_PC(frame) = bt->instptr;
Packit bf408e
Packit bf408e
	/* read rbp from stack for non active tasks */
Packit bf408e
	if (!(bt->flags & BT_DUMPFILE_SEARCH) && !bt->bptr) {
Packit bf408e
//		readmem(frame->regs.rsp, KVADDR, &bp,
Packit bf408e
		readmem(UNW_SP(frame), KVADDR, &bp,
Packit bf408e
	                sizeof(unsigned long), "reading bp", FAULT_ON_ERROR);
Packit bf408e
		frame->regs.rbp = bp;  /* fixme for x86 */
Packit bf408e
	}
Packit bf408e
Packit bf408e
	sp = value_search(UNW_PC(frame), &offset);
Packit bf408e
	if (!sp) {
Packit bf408e
		if (CRASHDEBUG(1))
Packit bf408e
		    fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", 
Packit bf408e
			UNW_PC(frame));
Packit bf408e
		goto bailout;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	/*
Packit bf408e
	 * If offset is zero, it means we have crossed over to the next
Packit bf408e
	 *  function. Recalculate by adjusting the text address
Packit bf408e
	 */
Packit bf408e
	if (!offset) {
Packit bf408e
		sp = value_search(UNW_PC(frame) - 1, &offset);
Packit bf408e
		if (!sp) {
Packit bf408e
			if (CRASHDEBUG(1))
Packit bf408e
				fprintf(fp, 
Packit bf408e
				    "unwind: cannot find symbol for PC: %lx\n",
Packit bf408e
					UNW_PC(frame)-1);
Packit bf408e
			goto bailout;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
		
Packit bf408e
Packit bf408e
Packit bf408e
        name = sp->name;
Packit bf408e
	fprintf(fp, " #%d [%016lx] %s at %016lx \n", level, UNW_SP(frame), name, UNW_PC(frame));
Packit bf408e
Packit bf408e
	if (CRASHDEBUG(2))
Packit bf408e
		fprintf(fp, "    < SP: %lx PC: %lx FP: %lx >\n", UNW_SP(frame), 
Packit bf408e
			UNW_PC(frame), frame->regs.rbp);
Packit bf408e
Packit bf408e
       	while ((UNW_SP(frame) < stacktop)
Packit bf408e
				&& !unwind(frame, is_ehframe) && UNW_PC(frame)) {
Packit bf408e
		/* To prevent rip pushed on IRQ stack being reported both
Packit bf408e
		 * both on the IRQ and process stacks
Packit bf408e
		 */
Packit bf408e
		if ((bt->flags & BT_IRQSTACK) && (UNW_SP(frame) >= stacktop - 16))
Packit bf408e
			break;
Packit bf408e
               	level++;
Packit bf408e
		sp = value_search(UNW_PC(frame), &offset);
Packit bf408e
		if (!sp) {
Packit bf408e
			if (CRASHDEBUG(1))
Packit bf408e
				fprintf(fp, 
Packit bf408e
				    "unwind: cannot find symbol for PC: %lx\n",
Packit bf408e
					UNW_PC(frame));
Packit bf408e
			break;
Packit bf408e
		}
Packit bf408e
Packit bf408e
		/*
Packit bf408e
		 * If offset is zero, it means we have crossed over to the next
Packit bf408e
		 *  function. Recalculate by adjusting the text address
Packit bf408e
		 */
Packit bf408e
		if (!offset) {
Packit bf408e
			sp = value_search(UNW_PC(frame) - 1, &offset);
Packit bf408e
			if (!sp) {
Packit bf408e
				if (CRASHDEBUG(1))
Packit bf408e
					fprintf(fp,
Packit bf408e
					    "unwind: cannot find symbol for PC: %lx\n",
Packit bf408e
						UNW_PC(frame)-1);
Packit bf408e
				goto bailout;
Packit bf408e
			}
Packit bf408e
		}
Packit bf408e
	        name = sp->name;
Packit bf408e
		fprintf(fp, "%s#%d [%016lx] %s at %016lx \n", level < 10 ? " " : "",
Packit bf408e
			level, UNW_SP(frame), name, UNW_PC(frame));
Packit bf408e
Packit bf408e
		if (CRASHDEBUG(2))
Packit bf408e
			fprintf(fp, "    < SP: %lx PC: %lx FP: %lx >\n", UNW_SP(frame), 
Packit bf408e
				UNW_PC(frame), frame->regs.rbp);
Packit bf408e
       	}
Packit bf408e
Packit bf408e
bailout:
Packit bf408e
	FREEBUF(frame);
Packit bf408e
	return ++level;
Packit bf408e
}
Packit bf408e
Packit bf408e
int 
Packit bf408e
dwarf_print_stack_entry(struct bt_info *bt, int level)
Packit bf408e
{
Packit bf408e
	unsigned long offset;
Packit bf408e
	struct syment *sp;
Packit bf408e
	char *name;
Packit bf408e
	struct unwind_frame_info *frame;
Packit bf408e
Packit bf408e
	frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info));
Packit bf408e
	UNW_SP(frame) = bt->stkptr;
Packit bf408e
	UNW_PC(frame) = bt->instptr;
Packit bf408e
Packit bf408e
	sp = value_search(UNW_PC(frame), &offset);
Packit bf408e
	if (!sp) {
Packit bf408e
		if (CRASHDEBUG(1))
Packit bf408e
		    fprintf(fp, "unwind: cannot find symbol for PC: %lx\n",
Packit bf408e
			UNW_PC(frame));
Packit bf408e
		goto bailout;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	/*
Packit bf408e
	 * If offset is zero, it means we have crossed over to the next
Packit bf408e
	 *  function. Recalculate by adjusting the text address
Packit bf408e
	 */
Packit bf408e
	if (!offset) {
Packit bf408e
		sp = value_search(UNW_PC(frame) - 1, &offset);
Packit bf408e
		if (!sp) {
Packit bf408e
			if (CRASHDEBUG(1))
Packit bf408e
				fprintf(fp,
Packit bf408e
				    "unwind: cannot find symbol for PC: %lx\n",
Packit bf408e
					UNW_PC(frame)-1);
Packit bf408e
			goto bailout;
Packit bf408e
		}
Packit bf408e
	}
Packit bf408e
        name = sp->name;
Packit bf408e
	fprintf(fp, " #%d [%016lx] %s at %016lx \n", level, UNW_SP(frame), name, UNW_PC(frame));
Packit bf408e
Packit bf408e
bailout:
Packit bf408e
	FREEBUF(frame);
Packit bf408e
	return level;
Packit bf408e
}
Packit bf408e
Packit bf408e
void
Packit bf408e
dwarf_debug(struct bt_info *bt)
Packit bf408e
{
Packit bf408e
	struct unwind_frame_info *frame;
Packit bf408e
	ulong bp;
Packit bf408e
	int is_ehframe = (!st->dwarf_debug_frame_size && st->dwarf_eh_frame_size);
Packit bf408e
Packit bf408e
	if (!bt->hp->eip) {
Packit bf408e
		dump_local_unwind_tables();
Packit bf408e
		return;
Packit bf408e
	}
Packit bf408e
Packit bf408e
	if (!(kt->flags & DWARF_UNWIND_CAPABLE)) {
Packit bf408e
		error(INFO, "not DWARF capable\n");
Packit bf408e
		return;
Packit bf408e
	}
Packit bf408e
Packit bf408e
        frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info));
Packit bf408e
Packit bf408e
	/*
Packit bf408e
	 *  XXX: This only works for the first PC/SP pair seen in a normal
Packit bf408e
	 *  backtrace, so it's not particularly helpful.  Ideally it should
Packit bf408e
         *  be capable to take any PC/SP pair in a stack, but it appears to
Packit bf408e
	 *  related to the rbp value. 
Packit bf408e
	 */
Packit bf408e
Packit bf408e
	UNW_PC(frame) = bt->hp->eip;
Packit bf408e
	UNW_SP(frame) = bt->hp->esp;
Packit bf408e
Packit bf408e
        readmem(UNW_SP(frame), KVADDR, &bp,
Packit bf408e
 		sizeof(unsigned long), "reading bp", FAULT_ON_ERROR);
Packit bf408e
        frame->regs.rbp = bp;  /* fixme for x86 */
Packit bf408e
Packit bf408e
	unwind(frame, is_ehframe);
Packit bf408e
Packit bf408e
	fprintf(fp, "frame size: %lx (%lx)\n", 
Packit bf408e
		(ulong)UNW_SP(frame), (ulong)UNW_SP(frame) - bt->hp->esp);
Packit bf408e
Packit bf408e
	FREEBUF(frame);
Packit bf408e
}
Packit bf408e
Packit bf408e
Packit bf408e
#endif