diff options
| -rw-r--r-- | arch/sh/include/asm/dwarf.h | 15 | ||||
| -rw-r--r-- | arch/sh/kernel/dwarf.c | 135 | ||||
| -rw-r--r-- | arch/sh/kernel/module.c | 32 |
3 files changed, 152 insertions, 30 deletions
diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index c367ed3373c5..aacdc746d07c 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h | |||
| @@ -241,6 +241,12 @@ struct dwarf_cie { | |||
| 241 | 241 | ||
| 242 | unsigned long flags; | 242 | unsigned long flags; |
| 243 | #define DWARF_CIE_Z_AUGMENTATION (1 << 0) | 243 | #define DWARF_CIE_Z_AUGMENTATION (1 << 0) |
| 244 | |||
| 245 | /* | ||
| 246 | * 'mod' will be non-NULL if this CIE came from a module's | ||
| 247 | * .eh_frame section. | ||
| 248 | */ | ||
| 249 | struct module *mod; | ||
| 244 | }; | 250 | }; |
| 245 | 251 | ||
| 246 | /** | 252 | /** |
| @@ -255,6 +261,12 @@ struct dwarf_fde { | |||
| 255 | unsigned char *instructions; | 261 | unsigned char *instructions; |
| 256 | unsigned char *end; | 262 | unsigned char *end; |
| 257 | struct list_head link; | 263 | struct list_head link; |
| 264 | |||
| 265 | /* | ||
| 266 | * 'mod' will be non-NULL if this FDE came from a module's | ||
| 267 | * .eh_frame section. | ||
| 268 | */ | ||
| 269 | struct module *mod; | ||
| 258 | }; | 270 | }; |
| 259 | 271 | ||
| 260 | /** | 272 | /** |
| @@ -364,6 +376,9 @@ static inline unsigned int DW_CFA_operand(unsigned long insn) | |||
| 364 | 376 | ||
| 365 | extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, | 377 | extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, |
| 366 | struct dwarf_frame *); | 378 | struct dwarf_frame *); |
| 379 | extern int dwarf_parse_section(char *, char *, struct module *); | ||
| 380 | extern void dwarf_module_unload(struct module *); | ||
| 381 | |||
| 367 | #endif /* !__ASSEMBLY__ */ | 382 | #endif /* !__ASSEMBLY__ */ |
| 368 | 383 | ||
| 369 | #define CFI_STARTPROC .cfi_startproc | 384 | #define CFI_STARTPROC .cfi_startproc |
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 | } |
