/*
* Copyright (C) 2009 Matt Fleming <matt@console-pimps.org>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* This is an implementation of a DWARF unwinder. Its main purpose is
* for generating stacktrace information. Based on the DWARF 3
* specification from http://www.dwarfstd.org.
*
* TODO:
* - DWARF64 doesn't work.
* - Registers with DWARF_VAL_OFFSET rules aren't handled properly.
*/
/* #define DEBUG */
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mempool.h>
#include <linux/mm.h>
#include <linux/elf.h>
#include <linux/ftrace.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/dwarf.h>
#include <asm/unwinder.h>
#include <asm/sections.h>
#include <asm/unaligned.h>
#include <asm/stacktrace.h>
/* Reserve enough memory for two stack frames */
#define DWARF_FRAME_MIN_REQ 2
/* ... with 4 registers per frame. */
#define DWARF_REG_MIN_REQ (DWARF_FRAME_MIN_REQ * 4)
static struct kmem_cache *dwarf_frame_cachep;
static mempool_t *dwarf_frame_pool;
static struct kmem_cache *dwarf_reg_cachep;
static mempool_t *dwarf_reg_pool;
static struct rb_root cie_root;
static DEFINE_SPINLOCK(dwarf_cie_lock);
static struct rb_root fde_root;
static DEFINE_SPINLOCK(dwarf_fde_lock);
static struct dwarf_cie *cached_cie;
static unsigned int dwarf_unwinder_ready;
/**
* dwarf_frame_alloc_reg - allocate memory for a DWARF register
* @frame: the DWARF frame whose list of registers we insert on
* @reg_num: the register number
*
* Allocate space for, and initialise, a dwarf reg from
* dwarf_reg_pool and insert it onto the (unsorted) linked-list of
* dwarf registers for @frame.
*
* Return the initialised DWARF reg.
*/
static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame,
unsigned int reg_num)
{
struct dwarf_reg *reg;
reg = mempool_alloc(dwarf_reg_pool, GFP_ATOMIC);
if (!reg) {
printk(KERN_WARNING "Unable to allocate a DWARF register\n");
/*
* Let's just bomb hard here, we have no way to
* gracefully recover.
*/
UNWINDER_BUG();
}
reg->number = reg_num;
reg->addr = 0;
reg->flags = 0;
list_add(®->link, &frame->reg_list);
return reg;
}
static void dwarf_frame_free_regs(struct dwarf_frame *frame)
{
struct dwarf_reg *reg, *n;
list_for_each_entry_safe(reg, n, &frame->reg_list, link) {
list_del(®->link);
mempool_free(reg, dwarf_reg_pool);
}
}
/**
* dwarf_frame_reg - return a DWARF register
* @frame: the DWARF frame to search in for @reg_num
* @reg_num: the register number to search for
*
* Lookup and return the dwarf reg @reg_num for this frame. Return
* NULL if @reg_num is an register invalid number.
*/
static struct dwarf_reg *dwarf_frame_reg(struct dwarf_frame *frame,
unsigned int reg_num)
{
struct dwarf_reg *reg;
list_for_each_entry(reg, &frame->reg_list, link) {
if (reg->number == reg_num)
return reg;
}
return NULL;
}
/**
* dwarf_read_addr - read dwarf data
* @src: source address of data
* @dst: destination address to store the data to
*
* Read 'n' bytes from @src, where 'n' is the size of an address on
* the native machine. We return the number of bytes read, which
* should always be 'n'. We also have to be careful when reading
* from @src and writing to @dst, because they can be arbitrarily
* aligned. Return 'n' - the number of bytes read.
*/
static inline int dwarf_read_addr(unsigned long *src, unsigned long *dst)
{
u32 val = get_unaligned(src);
put_unaligned(val, dst);
return sizeof(unsigned long *);
}
/**