diff options
Diffstat (limited to 'arch/sh/kernel/dwarf.c')
-rw-r--r-- | arch/sh/kernel/dwarf.c | 174 |
1 files changed, 120 insertions, 54 deletions
diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index e51168064e56..bd1c497280a6 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c | |||
@@ -39,10 +39,10 @@ static mempool_t *dwarf_frame_pool; | |||
39 | static struct kmem_cache *dwarf_reg_cachep; | 39 | static struct kmem_cache *dwarf_reg_cachep; |
40 | static mempool_t *dwarf_reg_pool; | 40 | static mempool_t *dwarf_reg_pool; |
41 | 41 | ||
42 | static LIST_HEAD(dwarf_cie_list); | 42 | static struct rb_root cie_root; |
43 | static DEFINE_SPINLOCK(dwarf_cie_lock); | 43 | static DEFINE_SPINLOCK(dwarf_cie_lock); |
44 | 44 | ||
45 | static LIST_HEAD(dwarf_fde_list); | 45 | static struct rb_root fde_root; |
46 | static DEFINE_SPINLOCK(dwarf_fde_lock); | 46 | static DEFINE_SPINLOCK(dwarf_fde_lock); |
47 | 47 | ||
48 | static struct dwarf_cie *cached_cie; | 48 | static struct dwarf_cie *cached_cie; |
@@ -301,7 +301,8 @@ static inline int dwarf_entry_len(char *addr, unsigned long *len) | |||
301 | */ | 301 | */ |
302 | static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) | 302 | static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) |
303 | { | 303 | { |
304 | struct dwarf_cie *cie; | 304 | struct rb_node **rb_node = &cie_root.rb_node; |
305 | struct dwarf_cie *cie = NULL; | ||
305 | unsigned long flags; | 306 | unsigned long flags; |
306 | 307 | ||
307 | spin_lock_irqsave(&dwarf_cie_lock, flags); | 308 | spin_lock_irqsave(&dwarf_cie_lock, flags); |
@@ -315,16 +316,24 @@ static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) | |||
315 | goto out; | 316 | goto out; |
316 | } | 317 | } |
317 | 318 | ||
318 | list_for_each_entry(cie, &dwarf_cie_list, link) { | 319 | while (*rb_node) { |
319 | if (cie->cie_pointer == cie_ptr) { | 320 | struct dwarf_cie *cie_tmp; |
320 | cached_cie = cie; | 321 | |
321 | break; | 322 | cie_tmp = rb_entry(*rb_node, struct dwarf_cie, node); |
323 | BUG_ON(!cie_tmp); | ||
324 | |||
325 | if (cie_ptr == cie_tmp->cie_pointer) { | ||
326 | cie = cie_tmp; | ||
327 | cached_cie = cie_tmp; | ||
328 | goto out; | ||
329 | } else { | ||
330 | if (cie_ptr < cie_tmp->cie_pointer) | ||
331 | rb_node = &(*rb_node)->rb_left; | ||
332 | else | ||
333 | rb_node = &(*rb_node)->rb_right; | ||
322 | } | 334 | } |
323 | } | 335 | } |
324 | 336 | ||
325 | /* Couldn't find the entry in the list. */ | ||
326 | if (&cie->link == &dwarf_cie_list) | ||
327 | cie = NULL; | ||
328 | out: | 337 | out: |
329 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); | 338 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); |
330 | return cie; | 339 | return cie; |
@@ -336,25 +345,34 @@ out: | |||
336 | */ | 345 | */ |
337 | struct dwarf_fde *dwarf_lookup_fde(unsigned long pc) | 346 | struct dwarf_fde *dwarf_lookup_fde(unsigned long pc) |
338 | { | 347 | { |
339 | struct dwarf_fde *fde; | 348 | struct rb_node **rb_node = &fde_root.rb_node; |
349 | struct dwarf_fde *fde = NULL; | ||
340 | unsigned long flags; | 350 | unsigned long flags; |
341 | 351 | ||
342 | spin_lock_irqsave(&dwarf_fde_lock, flags); | 352 | spin_lock_irqsave(&dwarf_fde_lock, flags); |
343 | 353 | ||
344 | list_for_each_entry(fde, &dwarf_fde_list, link) { | 354 | while (*rb_node) { |
345 | unsigned long start, end; | 355 | struct dwarf_fde *fde_tmp; |
356 | unsigned long tmp_start, tmp_end; | ||
346 | 357 | ||
347 | start = fde->initial_location; | 358 | fde_tmp = rb_entry(*rb_node, struct dwarf_fde, node); |
348 | end = fde->initial_location + fde->address_range; | 359 | BUG_ON(!fde_tmp); |
349 | 360 | ||
350 | if (pc >= start && pc < end) | 361 | tmp_start = fde_tmp->initial_location; |
351 | break; | 362 | tmp_end = fde_tmp->initial_location + fde_tmp->address_range; |
352 | } | ||
353 | 363 | ||
354 | /* Couldn't find the entry in the list. */ | 364 | if (pc < tmp_start) { |
355 | if (&fde->link == &dwarf_fde_list) | 365 | rb_node = &(*rb_node)->rb_left; |
356 | fde = NULL; | 366 | } else { |
367 | if (pc < tmp_end) { | ||
368 | fde = fde_tmp; | ||
369 | goto out; | ||
370 | } else | ||
371 | rb_node = &(*rb_node)->rb_right; | ||
372 | } | ||
373 | } | ||
357 | 374 | ||
375 | out: | ||
358 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); | 376 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); |
359 | 377 | ||
360 | return fde; | 378 | return fde; |
@@ -552,8 +570,8 @@ extern void ret_from_irq(void); | |||
552 | * on the callstack. Each of the lower (older) stack frames are | 570 | * on the callstack. Each of the lower (older) stack frames are |
553 | * linked via the "prev" member. | 571 | * linked via the "prev" member. |
554 | */ | 572 | */ |
555 | struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, | 573 | struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, |
556 | struct dwarf_frame *prev) | 574 | struct dwarf_frame *prev) |
557 | { | 575 | { |
558 | struct dwarf_frame *frame; | 576 | struct dwarf_frame *frame; |
559 | struct dwarf_cie *cie; | 577 | struct dwarf_cie *cie; |
@@ -708,6 +726,8 @@ bail: | |||
708 | static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | 726 | static int dwarf_parse_cie(void *entry, void *p, unsigned long len, |
709 | unsigned char *end, struct module *mod) | 727 | unsigned char *end, struct module *mod) |
710 | { | 728 | { |
729 | struct rb_node **rb_node = &cie_root.rb_node; | ||
730 | struct rb_node *parent; | ||
711 | struct dwarf_cie *cie; | 731 | struct dwarf_cie *cie; |
712 | unsigned long flags; | 732 | unsigned long flags; |
713 | int count; | 733 | int count; |
@@ -802,11 +822,30 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | |||
802 | cie->initial_instructions = p; | 822 | cie->initial_instructions = p; |
803 | cie->instructions_end = end; | 823 | cie->instructions_end = end; |
804 | 824 | ||
805 | cie->mod = mod; | ||
806 | |||
807 | /* Add to list */ | 825 | /* Add to list */ |
808 | spin_lock_irqsave(&dwarf_cie_lock, flags); | 826 | spin_lock_irqsave(&dwarf_cie_lock, flags); |
809 | list_add_tail(&cie->link, &dwarf_cie_list); | 827 | |
828 | while (*rb_node) { | ||
829 | struct dwarf_cie *cie_tmp; | ||
830 | |||
831 | cie_tmp = rb_entry(*rb_node, struct dwarf_cie, node); | ||
832 | |||
833 | parent = *rb_node; | ||
834 | |||
835 | if (cie->cie_pointer < cie_tmp->cie_pointer) | ||
836 | rb_node = &parent->rb_left; | ||
837 | else if (cie->cie_pointer >= cie_tmp->cie_pointer) | ||
838 | rb_node = &parent->rb_right; | ||
839 | else | ||
840 | WARN_ON(1); | ||
841 | } | ||
842 | |||
843 | rb_link_node(&cie->node, parent, rb_node); | ||
844 | rb_insert_color(&cie->node, &cie_root); | ||
845 | |||
846 | if (mod != NULL) | ||
847 | list_add_tail(&cie->link, &mod->arch.cie_list); | ||
848 | |||
810 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); | 849 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); |
811 | 850 | ||
812 | return 0; | 851 | return 0; |
@@ -816,6 +855,8 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, | |||
816 | void *start, unsigned long len, | 855 | void *start, unsigned long len, |
817 | unsigned char *end, struct module *mod) | 856 | unsigned char *end, struct module *mod) |
818 | { | 857 | { |
858 | struct rb_node **rb_node = &fde_root.rb_node; | ||
859 | struct rb_node *parent; | ||
819 | struct dwarf_fde *fde; | 860 | struct dwarf_fde *fde; |
820 | struct dwarf_cie *cie; | 861 | struct dwarf_cie *cie; |
821 | unsigned long flags; | 862 | unsigned long flags; |
@@ -863,11 +904,38 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, | |||
863 | fde->instructions = p; | 904 | fde->instructions = p; |
864 | fde->end = end; | 905 | fde->end = end; |
865 | 906 | ||
866 | fde->mod = mod; | ||
867 | |||
868 | /* Add to list. */ | 907 | /* Add to list. */ |
869 | spin_lock_irqsave(&dwarf_fde_lock, flags); | 908 | spin_lock_irqsave(&dwarf_fde_lock, flags); |
870 | list_add_tail(&fde->link, &dwarf_fde_list); | 909 | |
910 | while (*rb_node) { | ||
911 | struct dwarf_fde *fde_tmp; | ||
912 | unsigned long tmp_start, tmp_end; | ||
913 | unsigned long start, end; | ||
914 | |||
915 | fde_tmp = rb_entry(*rb_node, struct dwarf_fde, node); | ||
916 | |||
917 | start = fde->initial_location; | ||
918 | end = fde->initial_location + fde->address_range; | ||
919 | |||
920 | tmp_start = fde_tmp->initial_location; | ||
921 | tmp_end = fde_tmp->initial_location + fde_tmp->address_range; | ||
922 | |||
923 | parent = *rb_node; | ||
924 | |||
925 | if (start < tmp_start) | ||
926 | rb_node = &parent->rb_left; | ||
927 | else if (start >= tmp_end) | ||
928 | rb_node = &parent->rb_right; | ||
929 | else | ||
930 | WARN_ON(1); | ||
931 | } | ||
932 | |||
933 | rb_link_node(&fde->node, parent, rb_node); | ||
934 | rb_insert_color(&fde->node, &fde_root); | ||
935 | |||
936 | if (mod != NULL) | ||
937 | list_add_tail(&fde->link, &mod->arch.fde_list); | ||
938 | |||
871 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); | 939 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); |
872 | 940 | ||
873 | return 0; | 941 | return 0; |
@@ -912,19 +980,29 @@ static struct unwinder dwarf_unwinder = { | |||
912 | 980 | ||
913 | static void dwarf_unwinder_cleanup(void) | 981 | static void dwarf_unwinder_cleanup(void) |
914 | { | 982 | { |
915 | struct dwarf_cie *cie, *cie_tmp; | 983 | struct rb_node **fde_rb_node = &fde_root.rb_node; |
916 | struct dwarf_fde *fde, *fde_tmp; | 984 | struct rb_node **cie_rb_node = &cie_root.rb_node; |
917 | 985 | ||
918 | /* | 986 | /* |
919 | * Deallocate all the memory allocated for the DWARF unwinder. | 987 | * Deallocate all the memory allocated for the DWARF unwinder. |
920 | * Traverse all the FDE/CIE lists and remove and free all the | 988 | * Traverse all the FDE/CIE lists and remove and free all the |
921 | * memory associated with those data structures. | 989 | * memory associated with those data structures. |
922 | */ | 990 | */ |
923 | list_for_each_entry_safe(cie, cie_tmp, &dwarf_cie_list, link) | 991 | while (*fde_rb_node) { |
924 | kfree(cie); | 992 | struct dwarf_fde *fde; |
925 | 993 | ||
926 | list_for_each_entry_safe(fde, fde_tmp, &dwarf_fde_list, link) | 994 | fde = rb_entry(*fde_rb_node, struct dwarf_fde, node); |
995 | rb_erase(*fde_rb_node, &fde_root); | ||
927 | kfree(fde); | 996 | kfree(fde); |
997 | } | ||
998 | |||
999 | while (*cie_rb_node) { | ||
1000 | struct dwarf_cie *cie; | ||
1001 | |||
1002 | cie = rb_entry(*cie_rb_node, struct dwarf_cie, node); | ||
1003 | rb_erase(*cie_rb_node, &cie_root); | ||
1004 | kfree(cie); | ||
1005 | } | ||
928 | 1006 | ||
929 | kmem_cache_destroy(dwarf_reg_cachep); | 1007 | kmem_cache_destroy(dwarf_reg_cachep); |
930 | kmem_cache_destroy(dwarf_frame_cachep); | 1008 | kmem_cache_destroy(dwarf_frame_cachep); |
@@ -1024,6 +1102,8 @@ int module_dwarf_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, | |||
1024 | 1102 | ||
1025 | /* Did we find the .eh_frame section? */ | 1103 | /* Did we find the .eh_frame section? */ |
1026 | if (i != hdr->e_shnum) { | 1104 | if (i != hdr->e_shnum) { |
1105 | INIT_LIST_HEAD(&me->arch.cie_list); | ||
1106 | INIT_LIST_HEAD(&me->arch.fde_list); | ||
1027 | err = dwarf_parse_section((char *)start, (char *)end, me); | 1107 | err = dwarf_parse_section((char *)start, (char *)end, me); |
1028 | if (err) { | 1108 | if (err) { |
1029 | printk(KERN_WARNING "%s: failed to parse DWARF info\n", | 1109 | printk(KERN_WARNING "%s: failed to parse DWARF info\n", |
@@ -1044,38 +1124,26 @@ int module_dwarf_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, | |||
1044 | */ | 1124 | */ |
1045 | void module_dwarf_cleanup(struct module *mod) | 1125 | void module_dwarf_cleanup(struct module *mod) |
1046 | { | 1126 | { |
1047 | struct dwarf_fde *fde; | 1127 | struct dwarf_fde *fde, *ftmp; |
1048 | struct dwarf_cie *cie; | 1128 | struct dwarf_cie *cie, *ctmp; |
1049 | unsigned long flags; | 1129 | unsigned long flags; |
1050 | 1130 | ||
1051 | spin_lock_irqsave(&dwarf_cie_lock, flags); | 1131 | spin_lock_irqsave(&dwarf_cie_lock, flags); |
1052 | 1132 | ||
1053 | again_cie: | 1133 | list_for_each_entry_safe(cie, ctmp, &mod->arch.cie_list, link) { |
1054 | list_for_each_entry(cie, &dwarf_cie_list, link) { | ||
1055 | if (cie->mod == mod) | ||
1056 | break; | ||
1057 | } | ||
1058 | |||
1059 | if (&cie->link != &dwarf_cie_list) { | ||
1060 | list_del(&cie->link); | 1134 | list_del(&cie->link); |
1135 | rb_erase(&cie->node, &cie_root); | ||
1061 | kfree(cie); | 1136 | kfree(cie); |
1062 | goto again_cie; | ||
1063 | } | 1137 | } |
1064 | 1138 | ||
1065 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); | 1139 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); |
1066 | 1140 | ||
1067 | spin_lock_irqsave(&dwarf_fde_lock, flags); | 1141 | spin_lock_irqsave(&dwarf_fde_lock, flags); |
1068 | 1142 | ||
1069 | again_fde: | 1143 | list_for_each_entry_safe(fde, ftmp, &mod->arch.fde_list, link) { |
1070 | list_for_each_entry(fde, &dwarf_fde_list, link) { | ||
1071 | if (fde->mod == mod) | ||
1072 | break; | ||
1073 | } | ||
1074 | |||
1075 | if (&fde->link != &dwarf_fde_list) { | ||
1076 | list_del(&fde->link); | 1144 | list_del(&fde->link); |
1145 | rb_erase(&fde->node, &fde_root); | ||
1077 | kfree(fde); | 1146 | kfree(fde); |
1078 | goto again_fde; | ||
1079 | } | 1147 | } |
1080 | 1148 | ||
1081 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); | 1149 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); |
@@ -1094,8 +1162,6 @@ again_fde: | |||
1094 | static int __init dwarf_unwinder_init(void) | 1162 | static int __init dwarf_unwinder_init(void) |
1095 | { | 1163 | { |
1096 | int err; | 1164 | int err; |
1097 | INIT_LIST_HEAD(&dwarf_cie_list); | ||
1098 | INIT_LIST_HEAD(&dwarf_fde_list); | ||
1099 | 1165 | ||
1100 | dwarf_frame_cachep = kmem_cache_create("dwarf_frames", | 1166 | dwarf_frame_cachep = kmem_cache_create("dwarf_frames", |
1101 | sizeof(struct dwarf_frame), 0, | 1167 | sizeof(struct dwarf_frame), 0, |