Blame unwind_x86_32_64.c

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