diff options
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/dwarf.c | 135 | ||||
-rw-r--r-- | arch/sh/kernel/module.c | 32 |
2 files changed, 137 insertions, 30 deletions
diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 577302f31e6a..981315c6d656 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c | |||
@@ -655,7 +655,7 @@ bail: | |||
655 | } | 655 | } |
656 | 656 | ||
657 | static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | 657 | static int dwarf_parse_cie(void *entry, void *p, unsigned long len, |
658 | unsigned char *end) | 658 | unsigned char *end, struct module *mod) |
659 | { | 659 | { |
660 | struct dwarf_cie *cie; | 660 | struct dwarf_cie *cie; |
661 | unsigned long flags; | 661 | unsigned long flags; |
@@ -751,6 +751,8 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | |||
751 | cie->initial_instructions = p; | 751 | cie->initial_instructions = p; |
752 | cie->instructions_end = end; | 752 | cie->instructions_end = end; |
753 | 753 | ||
754 | cie->mod = mod; | ||
755 | |||
754 | /* Add to list */ | 756 | /* Add to list */ |
755 | spin_lock_irqsave(&dwarf_cie_lock, flags); | 757 | spin_lock_irqsave(&dwarf_cie_lock, flags); |
756 | list_add_tail(&cie->link, &dwarf_cie_list); | 758 | list_add_tail(&cie->link, &dwarf_cie_list); |
@@ -761,7 +763,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, | |||
761 | 763 | ||
762 | static int dwarf_parse_fde(void *entry, u32 entry_type, | 764 | static int dwarf_parse_fde(void *entry, u32 entry_type, |
763 | void *start, unsigned long len, | 765 | void *start, unsigned long len, |
764 | unsigned char *end) | 766 | unsigned char *end, struct module *mod) |
765 | { | 767 | { |
766 | struct dwarf_fde *fde; | 768 | struct dwarf_fde *fde; |
767 | struct dwarf_cie *cie; | 769 | struct dwarf_cie *cie; |
@@ -810,6 +812,8 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, | |||
810 | fde->instructions = p; | 812 | fde->instructions = p; |
811 | fde->end = end; | 813 | fde->end = end; |
812 | 814 | ||
815 | fde->mod = mod; | ||
816 | |||
813 | /* Add to list. */ | 817 | /* Add to list. */ |
814 | spin_lock_irqsave(&dwarf_fde_lock, flags); | 818 | spin_lock_irqsave(&dwarf_fde_lock, flags); |
815 | list_add_tail(&fde->link, &dwarf_fde_list); | 819 | list_add_tail(&fde->link, &dwarf_fde_list); |
@@ -875,15 +879,15 @@ static void dwarf_unwinder_cleanup(void) | |||
875 | } | 879 | } |
876 | 880 | ||
877 | /** | 881 | /** |
878 | * dwarf_unwinder_init - initialise the dwarf unwinder | 882 | * dwarf_parse_section - parse DWARF section |
883 | * @eh_frame_start: start address of the .eh_frame section | ||
884 | * @eh_frame_end: end address of the .eh_frame section | ||
885 | * @mod: the kernel module containing the .eh_frame section | ||
879 | * | 886 | * |
880 | * Build the data structures describing the .dwarf_frame section to | 887 | * Parse the information in a .eh_frame section. |
881 | * make it easier to lookup CIE and FDE entries. Because the | ||
882 | * .eh_frame section is packed as tightly as possible it is not | ||
883 | * easy to lookup the FDE for a given PC, so we build a list of FDE | ||
884 | * and CIE entries that make it easier. | ||
885 | */ | 888 | */ |
886 | static int __init dwarf_unwinder_init(void) | 889 | int dwarf_parse_section(char *eh_frame_start, char *eh_frame_end, |
890 | struct module *mod) | ||
887 | { | 891 | { |
888 | u32 entry_type; | 892 | u32 entry_type; |
889 | void *p, *entry; | 893 | void *p, *entry; |
@@ -891,29 +895,12 @@ static int __init dwarf_unwinder_init(void) | |||
891 | unsigned long len; | 895 | unsigned long len; |
892 | unsigned int c_entries, f_entries; | 896 | unsigned int c_entries, f_entries; |
893 | unsigned char *end; | 897 | unsigned char *end; |
894 | INIT_LIST_HEAD(&dwarf_cie_list); | ||
895 | INIT_LIST_HEAD(&dwarf_fde_list); | ||
896 | 898 | ||
897 | c_entries = 0; | 899 | c_entries = 0; |
898 | f_entries = 0; | 900 | f_entries = 0; |
899 | entry = &__start_eh_frame; | 901 | entry = eh_frame_start; |
900 | |||
901 | dwarf_frame_cachep = kmem_cache_create("dwarf_frames", | ||
902 | sizeof(struct dwarf_frame), 0, SLAB_PANIC, NULL); | ||
903 | dwarf_reg_cachep = kmem_cache_create("dwarf_regs", | ||
904 | sizeof(struct dwarf_reg), 0, SLAB_PANIC, NULL); | ||
905 | |||
906 | dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ, | ||
907 | mempool_alloc_slab, | ||
908 | mempool_free_slab, | ||
909 | dwarf_frame_cachep); | ||
910 | 902 | ||
911 | dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ, | 903 | while ((char *)entry < eh_frame_end) { |
912 | mempool_alloc_slab, | ||
913 | mempool_free_slab, | ||
914 | dwarf_reg_cachep); | ||
915 | |||
916 | while ((char *)entry < __stop_eh_frame) { | ||
917 | p = entry; | 904 | p = entry; |
918 | 905 | ||
919 | count = dwarf_entry_len(p, &len); | 906 | count = dwarf_entry_len(p, &len); |
@@ -925,6 +912,7 @@ static int __init dwarf_unwinder_init(void) | |||
925 | * entry and move to the next one because 'len' | 912 | * entry and move to the next one because 'len' |
926 | * tells us where our next entry is. | 913 | * tells us where our next entry is. |
927 | */ | 914 | */ |
915 | err = -EINVAL; | ||
928 | goto out; | 916 | goto out; |
929 | } else | 917 | } else |
930 | p += count; | 918 | p += count; |
@@ -936,13 +924,14 @@ static int __init dwarf_unwinder_init(void) | |||
936 | p += 4; | 924 | p += 4; |
937 | 925 | ||
938 | if (entry_type == DW_EH_FRAME_CIE) { | 926 | if (entry_type == DW_EH_FRAME_CIE) { |
939 | err = dwarf_parse_cie(entry, p, len, end); | 927 | err = dwarf_parse_cie(entry, p, len, end, mod); |
940 | if (err < 0) | 928 | if (err < 0) |
941 | goto out; | 929 | goto out; |
942 | else | 930 | else |
943 | c_entries++; | 931 | c_entries++; |
944 | } else { | 932 | } else { |
945 | err = dwarf_parse_fde(entry, entry_type, p, len, end); | 933 | err = dwarf_parse_fde(entry, entry_type, p, len, |
934 | end, mod); | ||
946 | if (err < 0) | 935 | if (err < 0) |
947 | goto out; | 936 | goto out; |
948 | else | 937 | else |
@@ -955,6 +944,92 @@ static int __init dwarf_unwinder_init(void) | |||
955 | printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n", | 944 | printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n", |
956 | c_entries, f_entries); | 945 | c_entries, f_entries); |
957 | 946 | ||
947 | return 0; | ||
948 | |||
949 | out: | ||
950 | return err; | ||
951 | } | ||
952 | |||
953 | /** | ||
954 | * dwarf_module_unload - remove FDE/CIEs associated with @mod | ||
955 | * @mod: the module that is being unloaded | ||
956 | * | ||
957 | * Remove any FDEs and CIEs from the global lists that came from | ||
958 | * @mod's .eh_frame section because @mod is being unloaded. | ||
959 | */ | ||
960 | void dwarf_module_unload(struct module *mod) | ||
961 | { | ||
962 | struct dwarf_fde *fde; | ||
963 | struct dwarf_cie *cie; | ||
964 | unsigned long flags; | ||
965 | |||
966 | spin_lock_irqsave(&dwarf_cie_lock, flags); | ||
967 | |||
968 | again_cie: | ||
969 | list_for_each_entry(cie, &dwarf_cie_list, link) { | ||
970 | if (cie->mod == mod) | ||
971 | break; | ||
972 | } | ||
973 | |||
974 | if (&cie->link != &dwarf_cie_list) { | ||
975 | list_del(&cie->link); | ||
976 | kfree(cie); | ||
977 | goto again_cie; | ||
978 | } | ||
979 | |||
980 | spin_unlock_irqrestore(&dwarf_cie_lock, flags); | ||
981 | |||
982 | spin_lock_irqsave(&dwarf_fde_lock, flags); | ||
983 | |||
984 | again_fde: | ||
985 | list_for_each_entry(fde, &dwarf_fde_list, link) { | ||
986 | if (fde->mod == mod) | ||
987 | break; | ||
988 | } | ||
989 | |||
990 | if (&fde->link != &dwarf_fde_list) { | ||
991 | list_del(&fde->link); | ||
992 | kfree(fde); | ||
993 | goto again_fde; | ||
994 | } | ||
995 | |||
996 | spin_unlock_irqrestore(&dwarf_fde_lock, flags); | ||
997 | } | ||
998 | |||
999 | /** | ||
1000 | * dwarf_unwinder_init - initialise the dwarf unwinder | ||
1001 | * | ||
1002 | * Build the data structures describing the .dwarf_frame section to | ||
1003 | * make it easier to lookup CIE and FDE entries. Because the | ||
1004 | * .eh_frame section is packed as tightly as possible it is not | ||
1005 | * easy to lookup the FDE for a given PC, so we build a list of FDE | ||
1006 | * and CIE entries that make it easier. | ||
1007 | */ | ||
1008 | static int __init dwarf_unwinder_init(void) | ||
1009 | { | ||
1010 | int err; | ||
1011 | INIT_LIST_HEAD(&dwarf_cie_list); | ||
1012 | INIT_LIST_HEAD(&dwarf_fde_list); | ||
1013 | |||
1014 | dwarf_frame_cachep = kmem_cache_create("dwarf_frames", | ||
1015 | sizeof(struct dwarf_frame), 0, SLAB_PANIC, NULL); | ||
1016 | dwarf_reg_cachep = kmem_cache_create("dwarf_regs", | ||
1017 | sizeof(struct dwarf_reg), 0, SLAB_PANIC, NULL); | ||
1018 | |||
1019 | dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ, | ||
1020 | mempool_alloc_slab, | ||
1021 | mempool_free_slab, | ||
1022 | dwarf_frame_cachep); | ||
1023 | |||
1024 | dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ, | ||
1025 | mempool_alloc_slab, | ||
1026 | mempool_free_slab, | ||
1027 | dwarf_reg_cachep); | ||
1028 | |||
1029 | err = dwarf_parse_section(__start_eh_frame, __stop_eh_frame, NULL); | ||
1030 | if (err) | ||
1031 | goto out; | ||
1032 | |||
958 | err = unwinder_register(&dwarf_unwinder); | 1033 | err = unwinder_register(&dwarf_unwinder); |
959 | if (err) | 1034 | if (err) |
960 | goto out; | 1035 | goto out; |
diff --git a/arch/sh/kernel/module.c b/arch/sh/kernel/module.c index c2efdcde266f..d297a148d16c 100644 --- a/arch/sh/kernel/module.c +++ b/arch/sh/kernel/module.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/string.h> | 32 | #include <linux/string.h> |
33 | #include <linux/kernel.h> | 33 | #include <linux/kernel.h> |
34 | #include <asm/unaligned.h> | 34 | #include <asm/unaligned.h> |
35 | #include <asm/dwarf.h> | ||
35 | 36 | ||
36 | void *module_alloc(unsigned long size) | 37 | void *module_alloc(unsigned long size) |
37 | { | 38 | { |
@@ -145,10 +146,41 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
145 | const Elf_Shdr *sechdrs, | 146 | const Elf_Shdr *sechdrs, |
146 | struct module *me) | 147 | struct module *me) |
147 | { | 148 | { |
149 | #ifdef CONFIG_DWARF_UNWINDER | ||
150 | unsigned int i, err; | ||
151 | unsigned long start, end; | ||
152 | char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
153 | |||
154 | start = end = 0; | ||
155 | |||
156 | for (i = 1; i < hdr->e_shnum; i++) { | ||
157 | /* Alloc bit cleared means "ignore it." */ | ||
158 | if ((sechdrs[i].sh_flags & SHF_ALLOC) | ||
159 | && !strcmp(secstrings+sechdrs[i].sh_name, ".eh_frame")) { | ||
160 | start = sechdrs[i].sh_addr; | ||
161 | end = start + sechdrs[i].sh_size; | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* Did we find the .eh_frame section? */ | ||
167 | if (i != hdr->e_shnum) { | ||
168 | err = dwarf_parse_section((char *)start, (char *)end, me); | ||
169 | if (err) | ||
170 | printk(KERN_WARNING "%s: failed to parse DWARF info\n", | ||
171 | me->name); | ||
172 | } | ||
173 | |||
174 | #endif /* CONFIG_DWARF_UNWINDER */ | ||
175 | |||
148 | return module_bug_finalize(hdr, sechdrs, me); | 176 | return module_bug_finalize(hdr, sechdrs, me); |
149 | } | 177 | } |
150 | 178 | ||
151 | void module_arch_cleanup(struct module *mod) | 179 | void module_arch_cleanup(struct module *mod) |
152 | { | 180 | { |
153 | module_bug_cleanup(mod); | 181 | module_bug_cleanup(mod); |
182 | |||
183 | #ifdef CONFIG_DWARF_UNWINDER | ||
184 | dwarf_module_unload(mod); | ||
185 | #endif /* CONFIG_DWARF_UNWINDER */ | ||
154 | } | 186 | } |