diff options
-rw-r--r-- | kernel/kprobes.c | 113 |
1 files changed, 111 insertions, 2 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 6fcf8dd148d0..d25a9ada3f8e 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -39,6 +39,8 @@ | |||
39 | #include <linux/moduleloader.h> | 39 | #include <linux/moduleloader.h> |
40 | #include <linux/kallsyms.h> | 40 | #include <linux/kallsyms.h> |
41 | #include <linux/freezer.h> | 41 | #include <linux/freezer.h> |
42 | #include <linux/seq_file.h> | ||
43 | #include <linux/debugfs.h> | ||
42 | #include <asm-generic/sections.h> | 44 | #include <asm-generic/sections.h> |
43 | #include <asm/cacheflush.h> | 45 | #include <asm/cacheflush.h> |
44 | #include <asm/errno.h> | 46 | #include <asm/errno.h> |
@@ -778,6 +780,12 @@ int __kprobes register_kretprobe(struct kretprobe *rp) | |||
778 | return -ENOSYS; | 780 | return -ENOSYS; |
779 | } | 781 | } |
780 | 782 | ||
783 | static int __kprobes pre_handler_kretprobe(struct kprobe *p, | ||
784 | struct pt_regs *regs) | ||
785 | { | ||
786 | return 0; | ||
787 | } | ||
788 | |||
781 | #endif /* ARCH_SUPPORTS_KRETPROBES */ | 789 | #endif /* ARCH_SUPPORTS_KRETPROBES */ |
782 | 790 | ||
783 | void __kprobes unregister_kretprobe(struct kretprobe *rp) | 791 | void __kprobes unregister_kretprobe(struct kretprobe *rp) |
@@ -815,7 +823,109 @@ static int __init init_kprobes(void) | |||
815 | return err; | 823 | return err; |
816 | } | 824 | } |
817 | 825 | ||
818 | __initcall(init_kprobes); | 826 | #ifdef CONFIG_DEBUG_FS |
827 | static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p, | ||
828 | const char *sym, int offset,char *modname) | ||
829 | { | ||
830 | char *kprobe_type; | ||
831 | |||
832 | if (p->pre_handler == pre_handler_kretprobe) | ||
833 | kprobe_type = "r"; | ||
834 | else if (p->pre_handler == setjmp_pre_handler) | ||
835 | kprobe_type = "j"; | ||
836 | else | ||
837 | kprobe_type = "k"; | ||
838 | if (sym) | ||
839 | seq_printf(pi, "%p %s %s+0x%x %s\n", p->addr, kprobe_type, | ||
840 | sym, offset, (modname ? modname : " ")); | ||
841 | else | ||
842 | seq_printf(pi, "%p %s %p\n", p->addr, kprobe_type, p->addr); | ||
843 | } | ||
844 | |||
845 | static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos) | ||
846 | { | ||
847 | return (*pos < KPROBE_TABLE_SIZE) ? pos : NULL; | ||
848 | } | ||
849 | |||
850 | static void __kprobes *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos) | ||
851 | { | ||
852 | (*pos)++; | ||
853 | if (*pos >= KPROBE_TABLE_SIZE) | ||
854 | return NULL; | ||
855 | return pos; | ||
856 | } | ||
857 | |||
858 | static void __kprobes kprobe_seq_stop(struct seq_file *f, void *v) | ||
859 | { | ||
860 | /* Nothing to do */ | ||
861 | } | ||
862 | |||
863 | static int __kprobes show_kprobe_addr(struct seq_file *pi, void *v) | ||
864 | { | ||
865 | struct hlist_head *head; | ||
866 | struct hlist_node *node; | ||
867 | struct kprobe *p, *kp; | ||
868 | const char *sym = NULL; | ||
869 | unsigned int i = *(loff_t *) v; | ||
870 | unsigned long size, offset = 0; | ||
871 | char *modname, namebuf[128]; | ||
872 | |||
873 | head = &kprobe_table[i]; | ||
874 | preempt_disable(); | ||
875 | hlist_for_each_entry_rcu(p, node, head, hlist) { | ||
876 | sym = kallsyms_lookup((unsigned long)p->addr, &size, | ||
877 | &offset, &modname, namebuf); | ||
878 | if (p->pre_handler == aggr_pre_handler) { | ||
879 | list_for_each_entry_rcu(kp, &p->list, list) | ||
880 | report_probe(pi, kp, sym, offset, modname); | ||
881 | } else | ||
882 | report_probe(pi, p, sym, offset, modname); | ||
883 | } | ||
884 | preempt_enable(); | ||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static struct seq_operations kprobes_seq_ops = { | ||
889 | .start = kprobe_seq_start, | ||
890 | .next = kprobe_seq_next, | ||
891 | .stop = kprobe_seq_stop, | ||
892 | .show = show_kprobe_addr | ||
893 | }; | ||
894 | |||
895 | static int __kprobes kprobes_open(struct inode *inode, struct file *filp) | ||
896 | { | ||
897 | return seq_open(filp, &kprobes_seq_ops); | ||
898 | } | ||
899 | |||
900 | static struct file_operations debugfs_kprobes_operations = { | ||
901 | .open = kprobes_open, | ||
902 | .read = seq_read, | ||
903 | .llseek = seq_lseek, | ||
904 | .release = seq_release, | ||
905 | }; | ||
906 | |||
907 | static int __kprobes debugfs_kprobe_init(void) | ||
908 | { | ||
909 | struct dentry *dir, *file; | ||
910 | |||
911 | dir = debugfs_create_dir("kprobes", NULL); | ||
912 | if (!dir) | ||
913 | return -ENOMEM; | ||
914 | |||
915 | file = debugfs_create_file("list", 0444, dir , 0 , | ||
916 | &debugfs_kprobes_operations); | ||
917 | if (!file) { | ||
918 | debugfs_remove(dir); | ||
919 | return -ENOMEM; | ||
920 | } | ||
921 | |||
922 | return 0; | ||
923 | } | ||
924 | |||
925 | late_initcall(debugfs_kprobe_init); | ||
926 | #endif /* CONFIG_DEBUG_FS */ | ||
927 | |||
928 | module_init(init_kprobes); | ||
819 | 929 | ||
820 | EXPORT_SYMBOL_GPL(register_kprobe); | 930 | EXPORT_SYMBOL_GPL(register_kprobe); |
821 | EXPORT_SYMBOL_GPL(unregister_kprobe); | 931 | EXPORT_SYMBOL_GPL(unregister_kprobe); |
@@ -824,4 +934,3 @@ EXPORT_SYMBOL_GPL(unregister_jprobe); | |||
824 | EXPORT_SYMBOL_GPL(jprobe_return); | 934 | EXPORT_SYMBOL_GPL(jprobe_return); |
825 | EXPORT_SYMBOL_GPL(register_kretprobe); | 935 | EXPORT_SYMBOL_GPL(register_kretprobe); |
826 | EXPORT_SYMBOL_GPL(unregister_kretprobe); | 936 | EXPORT_SYMBOL_GPL(unregister_kretprobe); |
827 | |||