Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
[linux-2.6.git] / arch / sh / kernel / dwarf.c
index bc4d8d7..94739ee 100644 (file)
 #include <linux/list.h>
 #include <linux/mempool.h>
 #include <linux/mm.h>
+#include <linux/elf.h>
+#include <linux/ftrace.h>
 #include <asm/dwarf.h>
 #include <asm/unwinder.h>
 #include <asm/sections.h>
 #include <asm/unaligned.h>
-#include <asm/dwarf.h>
 #include <asm/stacktrace.h>
 
 /* Reserve enough memory for two stack frames */
@@ -38,10 +39,10 @@ static mempool_t *dwarf_frame_pool;
 static struct kmem_cache *dwarf_reg_cachep;
 static mempool_t *dwarf_reg_pool;
 
-static LIST_HEAD(dwarf_cie_list);
+static struct rb_root cie_root;
 static DEFINE_SPINLOCK(dwarf_cie_lock);
 
-static LIST_HEAD(dwarf_fde_list);
+static struct rb_root fde_root;
 static DEFINE_SPINLOCK(dwarf_fde_lock);
 
 static struct dwarf_cie *cached_cie;
@@ -300,7 +301,8 @@ static inline int dwarf_entry_len(char *addr, unsigned long *len)
  */
 static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr)
 {
-       struct dwarf_cie *cie;
+       struct rb_node **rb_node = &cie_root.rb_node;
+       struct dwarf_cie *cie = NULL;
        unsigned long flags;
 
        spin_lock_irqsave(&dwarf_cie_lock, flags);
@@ -314,16 +316,24 @@ static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr)
                goto out;
        }
 
-       list_for_each_entry(cie, &dwarf_cie_list, link) {
-               if (cie->cie_pointer == cie_ptr) {
-                       cached_cie = cie;
-                       break;
+       while (*rb_node) {
+               struct dwarf_cie *cie_tmp;
+
+               cie_tmp = rb_entry(*rb_node, struct dwarf_cie, node);
+               BUG_ON(!cie_tmp);
+
+               if (cie_ptr == cie_tmp->cie_pointer) {
+                       cie = cie_tmp;
+                       cached_cie = cie_tmp;
+                       goto out;
+               } else {
+                       if (cie_ptr < cie_tmp->cie_pointer)
+                               rb_node = &(*rb_node)->rb_left;
+                       else
+                               rb_node = &(*rb_node)->rb_right;
                }
        }
 
-       /* Couldn't find the entry in the list. */
-       if (&cie->link == &dwarf_cie_list)
-               cie = NULL;
 out:
        spin_unlock_irqrestore(&dwarf_cie_lock, flags);
        return cie;
@@ -335,25 +345,34 @@ out:
  */
 struct dwarf_fde *dwarf_lookup_fde(unsigned long pc)
 {
-       struct dwarf_fde *fde;
+       struct rb_node **rb_node = &fde_root.rb_node;
+       struct dwarf_fde *fde = NULL;
        unsigned long flags;
 
        spin_lock_irqsave(&dwarf_fde_lock, flags);
 
-       list_for_each_entry(fde, &dwarf_fde_list, link) {
-               unsigned long start, end;
+       while (*rb_node) {
+               struct dwarf_fde *fde_tmp;
+               unsigned long tmp_start, tmp_end;
 
-               start = fde->initial_location;
-               end = fde->initial_location + fde->address_range;
+               fde_tmp = rb_entry(*rb_node, struct dwarf_fde, node);
+               BUG_ON(!fde_tmp);
 
-               if (pc >= start && pc < end)
-                       break;
-       }
+               tmp_start = fde_tmp->initial_location;
+               tmp_end = fde_tmp->initial_location + fde_tmp->address_range;
 
-       /* Couldn't find the entry in the list. */
-       if (&fde->link == &dwarf_fde_list)
-               fde = NULL;
+               if (pc < tmp_start) {
+                       rb_node = &(*rb_node)->rb_left;
+               } else {
+                       if (pc < tmp_end) {
+                               fde = fde_tmp;
+                               goto out;
+                       } else
+                               rb_node = &(*rb_node)->rb_right;
+               }
+       }
 
+out:
        spin_unlock_irqrestore(&dwarf_fde_lock, flags);
 
        return fde;
@@ -530,7 +549,20 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start,
 }
 
 /**
- *     dwarf_unwind_stack - recursively unwind the stack
+ *     dwarf_free_frame - free the memory allocated for @frame
+ *     @frame: the frame to free
+ */
+void dwarf_free_frame(struct dwarf_frame *frame)
+{
+       dwarf_frame_free_regs(frame);
+       mempool_free(frame, dwarf_frame_pool);
+}
+
+extern void ret_from_irq(void);
+
+/**
+ *     dwarf_unwind_stack - unwind the stack
+ *
  *     @pc: address of the function to unwind
  *     @prev: struct dwarf_frame of the previous stackframe on the callstack
  *
@@ -538,8 +570,8 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start,
  *     on the callstack. Each of the lower (older) stack frames are
  *     linked via the "prev" member.
  */
-struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
-                                       struct dwarf_frame *prev)
+struct dwarf_frame *dwarf_unwind_stack(unsigned long pc,
+                                      struct dwarf_frame *prev)
 {
        struct dwarf_frame *frame;
        struct dwarf_cie *cie;
@@ -548,16 +580,37 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
        unsigned long addr;
 
        /*
-        * If this is the first invocation of this recursive function we
-        * need get the contents of a physical register to get the CFA
-        * in order to begin the virtual unwinding of the stack.
+        * If we're starting at the top of the stack we need get the
+        * contents of a physical register to get the CFA in order to
+        * begin the virtual unwinding of the stack.
         *
         * NOTE: the return address is guaranteed to be setup by the
         * time this function makes its first function call.
         */
-       if (!pc && !prev)
+       if (!pc || !prev)
                pc = (unsigned long)current_text_addr();
 
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+       /*
+        * If our stack has been patched by the function graph tracer
+        * then we might see the address of return_to_handler() where we
+        * expected to find the real return address.
+        */
+       if (pc == (unsigned long)&return_to_handler) {
+               int index = current->curr_ret_stack;
+
+               /*
+                * We currently have no way of tracking how many
+                * return_to_handler()'s we've seen. If there is more
+                * than one patched return address on our stack,
+                * complain loudly.
+                */
+               WARN_ON(index > 0);
+
+               pc = current->ret_stack[index].ret;
+       }
+#endif
+
        frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC);
        if (!frame) {
                printk(KERN_ERR "Unable to allocate a dwarf frame\n");
@@ -572,9 +625,8 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
        fde = dwarf_lookup_fde(pc);
        if (!fde) {
                /*
-                * This is our normal exit path - the one that stops the
-                * recursion. There's two reasons why we might exit
-                * here,
+                * This is our normal exit path. There are two reasons
+                * why we might exit here,
                 *
                 *      a) pc has no asscociated DWARF frame info and so
                 *      we don't know how to unwind this frame. This is
@@ -616,10 +668,10 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
 
                } else {
                        /*
-                        * Again, this is the first invocation of this
-                        * recurisve function. We need to physically
-                        * read the contents of a register in order to
-                        * get the Canonical Frame Address for this
+                        * Again, we're starting from the top of the
+                        * stack. We need to physically read
+                        * the contents of a register in order to get
+                        * the Canonical Frame Address for this
                         * function.
                         */
                        frame->cfa = dwarf_read_arch_reg(frame->cfa_register);
@@ -646,17 +698,36 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
        addr = frame->cfa + reg->addr;
        frame->return_addr = __raw_readl(addr);
 
+       /*
+        * Ah, the joys of unwinding through interrupts.
+        *
+        * Interrupts are tricky - the DWARF info needs to be _really_
+        * accurate and unfortunately I'm seeing a lot of bogus DWARF
+        * info. For example, I've seen interrupts occur in epilogues
+        * just after the frame pointer (r14) had been restored. The
+        * problem was that the DWARF info claimed that the CFA could be
+        * reached by using the value of the frame pointer before it was
+        * restored.
+        *
+        * So until the compiler can be trusted to produce reliable
+        * DWARF info when it really matters, let's stop unwinding once
+        * we've calculated the function that was interrupted.
+        */
+       if (prev && prev->pc == (unsigned long)ret_from_irq)
+               frame->return_addr = 0;
+
        return frame;
 
 bail:
-       dwarf_frame_free_regs(frame);
-       mempool_free(frame, dwarf_frame_pool);
+       dwarf_free_frame(frame);
        return NULL;
 }
 
 static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
-                          unsigned char *end)
+                          unsigned char *end, struct module *mod)
 {
+       struct rb_node **rb_node = &cie_root.rb_node;
+       struct rb_node *parent = *rb_node;
        struct dwarf_cie *cie;
        unsigned long flags;
        int count;
@@ -753,7 +824,28 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
 
        /* Add to list */
        spin_lock_irqsave(&dwarf_cie_lock, flags);
-       list_add_tail(&cie->link, &dwarf_cie_list);
+
+       while (*rb_node) {
+               struct dwarf_cie *cie_tmp;
+
+               cie_tmp = rb_entry(*rb_node, struct dwarf_cie, node);
+
+               parent = *rb_node;
+
+               if (cie->cie_pointer < cie_tmp->cie_pointer)
+                       rb_node = &parent->rb_left;
+               else if (cie->cie_pointer >= cie_tmp->cie_pointer)
+                       rb_node = &parent->rb_right;
+               else
+                       WARN_ON(1);
+       }
+
+       rb_link_node(&cie->node, parent, rb_node);
+       rb_insert_color(&cie->node, &cie_root);
+
+       if (mod != NULL)
+               list_add_tail(&cie->link, &mod->arch.cie_list);
+
        spin_unlock_irqrestore(&dwarf_cie_lock, flags);
 
        return 0;
@@ -761,8 +853,10 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
 
 static int dwarf_parse_fde(void *entry, u32 entry_type,
                           void *start, unsigned long len,
-                          unsigned char *end)
+                          unsigned char *end, struct module *mod)
 {
+       struct rb_node **rb_node = &fde_root.rb_node;
+       struct rb_node *parent = *rb_node;
        struct dwarf_fde *fde;
        struct dwarf_cie *cie;
        unsigned long flags;
@@ -812,7 +906,36 @@ static int dwarf_parse_fde(void *entry, u32 entry_type,
 
        /* Add to list. */
        spin_lock_irqsave(&dwarf_fde_lock, flags);
-       list_add_tail(&fde->link, &dwarf_fde_list);
+
+       while (*rb_node) {
+               struct dwarf_fde *fde_tmp;
+               unsigned long tmp_start, tmp_end;
+               unsigned long start, end;
+
+               fde_tmp = rb_entry(*rb_node, struct dwarf_fde, node);
+
+               start = fde->initial_location;
+               end = fde->initial_location + fde->address_range;
+
+               tmp_start = fde_tmp->initial_location;
+               tmp_end = fde_tmp->initial_location + fde_tmp->address_range;
+
+               parent = *rb_node;
+
+               if (start < tmp_start)
+                       rb_node = &parent->rb_left;
+               else if (start >= tmp_end)
+                       rb_node = &parent->rb_right;
+               else
+                       WARN_ON(1);
+       }
+
+       rb_link_node(&fde->node, parent, rb_node);
+       rb_insert_color(&fde->node, &fde_root);
+
+       if (mod != NULL)
+               list_add_tail(&fde->link, &mod->arch.fde_list);
+
        spin_unlock_irqrestore(&dwarf_fde_lock, flags);
 
        return 0;
@@ -833,10 +956,8 @@ static void dwarf_unwinder_dump(struct task_struct *task,
        while (1) {
                frame = dwarf_unwind_stack(return_addr, _frame);
 
-               if (_frame) {
-                       dwarf_frame_free_regs(_frame);
-                       mempool_free(_frame, dwarf_frame_pool);
-               }
+               if (_frame)
+                       dwarf_free_frame(_frame);
 
                _frame = frame;
 
@@ -846,6 +967,9 @@ static void dwarf_unwinder_dump(struct task_struct *task,
                return_addr = frame->return_addr;
                ops->address(data, return_addr, 1);
        }
+
+       if (frame)
+               dwarf_free_frame(frame);
 }
 
 static struct unwinder dwarf_unwinder = {
@@ -856,67 +980,57 @@ static struct unwinder dwarf_unwinder = {
 
 static void dwarf_unwinder_cleanup(void)
 {
-       struct dwarf_cie *cie;
-       struct dwarf_fde *fde;
+       struct rb_node **fde_rb_node = &fde_root.rb_node;
+       struct rb_node **cie_rb_node = &cie_root.rb_node;
 
        /*
         * Deallocate all the memory allocated for the DWARF unwinder.
         * Traverse all the FDE/CIE lists and remove and free all the
         * memory associated with those data structures.
         */
-       list_for_each_entry(cie, &dwarf_cie_list, link)
-               kfree(cie);
+       while (*fde_rb_node) {
+               struct dwarf_fde *fde;
 
-       list_for_each_entry(fde, &dwarf_fde_list, link)
+               fde = rb_entry(*fde_rb_node, struct dwarf_fde, node);
+               rb_erase(*fde_rb_node, &fde_root);
                kfree(fde);
+       }
+
+       while (*cie_rb_node) {
+               struct dwarf_cie *cie;
+
+               cie = rb_entry(*cie_rb_node, struct dwarf_cie, node);
+               rb_erase(*cie_rb_node, &cie_root);
+               kfree(cie);
+       }
 
        kmem_cache_destroy(dwarf_reg_cachep);
        kmem_cache_destroy(dwarf_frame_cachep);
 }
 
 /**
- *     dwarf_unwinder_init - initialise the dwarf unwinder
+ *     dwarf_parse_section - parse DWARF section
+ *     @eh_frame_start: start address of the .eh_frame section
+ *     @eh_frame_end: end address of the .eh_frame section
+ *     @mod: the kernel module containing the .eh_frame section
  *
- *     Build the data structures describing the .dwarf_frame section to
- *     make it easier to lookup CIE and FDE entries. Because the
- *     .eh_frame section is packed as tightly as possible it is not
- *     easy to lookup the FDE for a given PC, so we build a list of FDE
- *     and CIE entries that make it easier.
+ *     Parse the information in a .eh_frame section.
  */
-static int __init dwarf_unwinder_init(void)
+static int dwarf_parse_section(char *eh_frame_start, char *eh_frame_end,
+                              struct module *mod)
 {
        u32 entry_type;
        void *p, *entry;
        int count, err = 0;
-       unsigned long len;
+       unsigned long len = 0;
        unsigned int c_entries, f_entries;
        unsigned char *end;
-       INIT_LIST_HEAD(&dwarf_cie_list);
-       INIT_LIST_HEAD(&dwarf_fde_list);
 
        c_entries = 0;
        f_entries = 0;
-       entry = &__start_eh_frame;
-
-       dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
-                       sizeof(struct dwarf_frame), 0,
-                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
+       entry = eh_frame_start;
 
-       dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
-                       sizeof(struct dwarf_reg), 0,
-                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
-
-       dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ,
-                                         mempool_alloc_slab,
-                                         mempool_free_slab,
-                                         dwarf_frame_cachep);
-
-       dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ,
-                                        mempool_alloc_slab,
-                                        mempool_free_slab,
-                                        dwarf_reg_cachep);
-
-       while ((char *)entry < __stop_eh_frame) {
+       while ((char *)entry < eh_frame_end) {
                p = entry;
 
                count = dwarf_entry_len(p, &len);
@@ -928,6 +1042,7 @@ static int __init dwarf_unwinder_init(void)
                         * entry and move to the next one because 'len'
                         * tells us where our next entry is.
                         */
+                       err = -EINVAL;
                        goto out;
                } else
                        p += count;
@@ -939,13 +1054,14 @@ static int __init dwarf_unwinder_init(void)
                p += 4;
 
                if (entry_type == DW_EH_FRAME_CIE) {
-                       err = dwarf_parse_cie(entry, p, len, end);
+                       err = dwarf_parse_cie(entry, p, len, end, mod);
                        if (err < 0)
                                goto out;
                        else
                                c_entries++;
                } else {
-                       err = dwarf_parse_fde(entry, entry_type, p, len, end);
+                       err = dwarf_parse_fde(entry, entry_type, p, len,
+                                             end, mod);
                        if (err < 0)
                                goto out;
                        else
@@ -958,6 +1074,117 @@ static int __init dwarf_unwinder_init(void)
        printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n",
               c_entries, f_entries);
 
+       return 0;
+
+out:
+       return err;
+}
+
+#ifdef CONFIG_MODULES
+int module_dwarf_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+                         struct module *me)
+{
+       unsigned int i, err;
+       unsigned long start, end;
+       char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+       start = end = 0;
+
+       for (i = 1; i < hdr->e_shnum; i++) {
+               /* Alloc bit cleared means "ignore it." */
+               if ((sechdrs[i].sh_flags & SHF_ALLOC)
+                   && !strcmp(secstrings+sechdrs[i].sh_name, ".eh_frame")) {
+                       start = sechdrs[i].sh_addr;
+                       end = start + sechdrs[i].sh_size;
+                       break;
+               }
+       }
+
+       /* Did we find the .eh_frame section? */
+       if (i != hdr->e_shnum) {
+               INIT_LIST_HEAD(&me->arch.cie_list);
+               INIT_LIST_HEAD(&me->arch.fde_list);
+               err = dwarf_parse_section((char *)start, (char *)end, me);
+               if (err) {
+                       printk(KERN_WARNING "%s: failed to parse DWARF info\n",
+                              me->name);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ *     module_dwarf_cleanup - remove FDE/CIEs associated with @mod
+ *     @mod: the module that is being unloaded
+ *
+ *     Remove any FDEs and CIEs from the global lists that came from
+ *     @mod's .eh_frame section because @mod is being unloaded.
+ */
+void module_dwarf_cleanup(struct module *mod)
+{
+       struct dwarf_fde *fde, *ftmp;
+       struct dwarf_cie *cie, *ctmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dwarf_cie_lock, flags);
+
+       list_for_each_entry_safe(cie, ctmp, &mod->arch.cie_list, link) {
+               list_del(&cie->link);
+               rb_erase(&cie->node, &cie_root);
+               kfree(cie);
+       }
+
+       spin_unlock_irqrestore(&dwarf_cie_lock, flags);
+
+       spin_lock_irqsave(&dwarf_fde_lock, flags);
+
+       list_for_each_entry_safe(fde, ftmp, &mod->arch.fde_list, link) {
+               list_del(&fde->link);
+               rb_erase(&fde->node, &fde_root);
+               kfree(fde);
+       }
+
+       spin_unlock_irqrestore(&dwarf_fde_lock, flags);
+}
+#endif /* CONFIG_MODULES */
+
+/**
+ *     dwarf_unwinder_init - initialise the dwarf unwinder
+ *
+ *     Build the data structures describing the .dwarf_frame section to
+ *     make it easier to lookup CIE and FDE entries. Because the
+ *     .eh_frame section is packed as tightly as possible it is not
+ *     easy to lookup the FDE for a given PC, so we build a list of FDE
+ *     and CIE entries that make it easier.
+ */
+static int __init dwarf_unwinder_init(void)
+{
+       int err;
+
+       dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
+                       sizeof(struct dwarf_frame), 0,
+                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
+
+       dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
+                       sizeof(struct dwarf_reg), 0,
+                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
+
+       dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ,
+                                         mempool_alloc_slab,
+                                         mempool_free_slab,
+                                         dwarf_frame_cachep);
+
+       dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ,
+                                        mempool_alloc_slab,
+                                        mempool_free_slab,
+                                        dwarf_reg_cachep);
+
+       err = dwarf_parse_section(__start_eh_frame, __stop_eh_frame, NULL);
+       if (err)
+               goto out;
+
        err = unwinder_register(&dwarf_unwinder);
        if (err)
                goto out;