lkml.org 
[lkml]   [2009]   [Sep]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    Date
    Subject[Demo/lkm] Kcoredump -- Do coredump in any where of kernel(not same with kcore)
    Hi guys,

    Kcoredump do a coredump in most part of kernel (it use kprobe, it can
    be set most of part of kernel). For example:
    insmod kcoredump.ko name=do_fork offset=11
    gdb ./vmlinux /proc/kcoredump
    Core was generated by `'.
    [New process 0]
    #0 do_fork (clone_flags=18874385, stack_start=3219612376, regs=0xc6c8ffb4,
    stack_size=0, parent_tidptr=0x0, child_tidptr=0xb764b758)
    at /home/teawater/kernel/linux-2.6/kernel/fork.c:1343

    warning: Source file is more recent than executable.
    1343 if (clone_flags & CLONE_NEWUSER) {
    (gdb) bt
    #0 do_fork (clone_flags=18874385, stack_start=3219612376, regs=0xc6c8ffb4,
    stack_size=0, parent_tidptr=0x0, child_tidptr=0xb764b758)
    at /home/teawater/kernel/linux-2.6/kernel/fork.c:1343
    #1 0xc01016d5 in sys_clone (regs=0xc6c8ffb4)
    at /home/teawater/kernel/linux-2.6/arch/x86/kernel/process_32.c:445
    #2 0xc0102da1 in system_call ()
    at /home/teawater/kernel/linux-2.6/arch/x86/kernel/entry_32.S:529
    #3 0x00000000 in ?? ()

    Now, it is just a demo version. It just support x86_32. But make it
    support other arch is not very hard.

    Thanks,
    Hui
    /*
    * Kernel core dump.
    *
    * This program is free software; you can redistribute it and/or modify
    * it under the terms of the GNU General Public License as published by
    * the Free Software Foundation; either version 2 of the License, or
    * (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    *
    * Copyright (C) Hui Zhu (teawater@gmail.com), 2009
    *
    * 2002-09-22 Demo release. Just support X86_32.
    */

    #include <linux/version.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/kprobes.h>
    #include <linux/elfcore.h>
    #include <linux/init.h>
    #include <linux/uaccess.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>

    #ifndef CONFIG_KPROBES
    #error "Linux Kernel doesn't support KPROBES. Please open it in 'General setup->Kprobes'."
    #endif

    #define KNAME_NOT_SET "NOT SET"
    #define ELFNOTELEN(datalen) (sizeof(struct elf_note) + roundup(sizeof ("CORE"), 4) + roundup(datalen, 4))
    #define STACKLEN 512
    #define KBUFLEN (sizeof(struct elfhdr) + sizeof(struct elf_phdr) * 2 + ELFNOTELEN(sizeof(struct elf_prstatus)) + ELFNOTELEN(sizeof(struct elf_prpsinfo)) + STACKLEN)

    static char *kname = KNAME_NOT_SET;
    static unsigned int koffset = 0;
    static unsigned int kaddr = 0;
    static struct proc_dir_entry *kcoredump_file = NULL;

    static uint8_t kbuf[KBUFLEN];
    static struct elf_prstatus *kpstat = NULL;
    static struct elf_phdr *kphdr = NULL;
    static uint8_t *kstack;

    static int
    handler_pre(struct kprobe *p, struct pt_regs *regs)
    {
    static int dumped = 0;
    static DEFINE_SPINLOCK(dumped_lock);

    spin_lock(&dumped_lock);
    if (dumped) {
    spin_unlock(&dumped_lock);
    return 0;
    }
    dumped = 1;
    spin_unlock(&dumped_lock);

    #ifdef CONFIG_X86_32
    /* Set regs to kpstat->pr_reg. */
    #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24))
    kpstat->pr_reg[0] = regs->bx;
    kpstat->pr_reg[1] = regs->cx;
    kpstat->pr_reg[2] = regs->dx;
    kpstat->pr_reg[3] = regs->si;
    kpstat->pr_reg[4] = regs->di;
    kpstat->pr_reg[5] = regs->bp;
    kpstat->pr_reg[6] = regs->ax;
    kpstat->pr_reg[7] = regs->ds;
    kpstat->pr_reg[8] = regs->es;
    kpstat->pr_reg[9] = regs->fs;
    #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
    kpstat->pr_reg[10] = regs->gs;
    #else
    kpstat->pr_reg[10] = 0;
    #endif
    kpstat->pr_reg[11] = regs->orig_ax;
    kpstat->pr_reg[12] = regs->ip - 1;
    kpstat->pr_reg[13] = regs->cs;
    kpstat->pr_reg[14] = regs->flags;
    kpstat->pr_reg[15] = (unsigned long)&regs->sp;
    kpstat->pr_reg[16] = 0;
    #else
    kpstat->pr_reg[0] = regs->ebx;
    kpstat->pr_reg[1] = regs->ecx;
    kpstat->pr_reg[2] = regs->edx;
    kpstat->pr_reg[3] = regs->esi;
    kpstat->pr_reg[4] = regs->edi;
    kpstat->pr_reg[5] = regs->ebp;
    kpstat->pr_reg[6] = regs->eax;
    kpstat->pr_reg[7] = regs->xds;
    kpstat->pr_reg[8] = regs->xes;
    kpstat->pr_reg[9] = regs->xfs;
    /* kpstat->pr_reg[10] = regs->gs; */
    kpstat->pr_reg[11] = regs->orig_eax;
    kpstat->pr_reg[12] = regs->eip - 1;
    kpstat->pr_reg[13] = regs->xcs;
    kpstat->pr_reg[14] = regs->eflags;
    kpstat->pr_reg[15] = (unsigned long)&regs->esp;
    kpstat->pr_reg[16] = 0;
    #endif
    /* Set kpstat->pr_reg[15] to kphdr->p_vaddr. */
    kphdr->p_vaddr = kpstat->pr_reg[15] & ~63;

    #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25))
    if (probe_kernel_read (kstack, (void *)kphdr->p_vaddr, STACKLEN))
    memset (kstack, 0, STACKLEN);
    #else
    memcpy (kstack, (void *)kphdr->p_vaddr, STACKLEN);
    #endif

    return 0;
    #else
    #error "This architecture doesn't support kcoredump."
    #endif
    }

    static void
    handler_post(struct kprobe *p, struct pt_regs *regs,
    unsigned long flags)
    {
    }

    static int
    handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
    {
    printk(KERN_ERR "kcoredump: get fault addr = 0x%p, trap #%dn\n",
    p->addr, trapnr);
    return 0;
    }

    static struct kprobe kp = {
    .pre_handler = handler_pre,
    .post_handler = handler_post,
    .fault_handler = handler_fault,
    };

    static int
    fill_elf_note (uint8_t *p, const char *name, int type, void **data, int data_len)
    {
    int ret = 0;
    struct elf_note *en = (struct elf_note *) p;

    en = (struct elf_note *) p;
    p += sizeof(struct elf_note);
    ret += sizeof(struct elf_note);
    en->n_namesz = strlen(name) + 1;
    en->n_type = type;
    en->n_descsz = data_len;

    memcpy (p, name, en->n_namesz);
    p += en->n_namesz;
    p = (uint8_t *) roundup((unsigned long) p, 4);
    ret += roundup((en->n_namesz), 4);

    *data = p;
    p += data_len;
    p = (uint8_t *) roundup((unsigned long) p, 4);
    ret += roundup(data_len, 4);

    return ret;
    }

    static int
    init_kbuf (void)
    {
    uint8_t *wp = kbuf;
    struct elfhdr *elf;
    struct elf_phdr *nhdr;
    struct elf_prpsinfo *pinfo;

    /* elf head */
    elf = (struct elfhdr *) wp;
    wp += sizeof(struct elfhdr);
    memset(elf, 0, sizeof(*elf));
    memcpy(elf->e_ident, ELFMAG, SELFMAG);
    elf->e_ident[EI_CLASS] = ELF_CLASS;
    elf->e_ident[EI_DATA] = ELF_DATA;
    elf->e_ident[EI_VERSION] = EV_CURRENT;
    elf->e_ident[EI_OSABI] = ELF_OSABI;
    elf->e_type = ET_CORE;
    elf->e_machine = ELF_ARCH;
    elf->e_version = EV_CURRENT;
    elf->e_phoff = sizeof(struct elfhdr);
    elf->e_flags = 0;
    elf->e_ehsize = sizeof(struct elfhdr);
    elf->e_phentsize = sizeof(struct elf_phdr);
    /* sections number (vma + 1) */
    elf->e_phnum = 1 + 1;

    /* nhdr */
    nhdr = (struct elf_phdr *) wp;
    wp += sizeof(struct elf_phdr);
    nhdr->p_type = PT_NOTE;
    nhdr->p_offset = sizeof(struct elfhdr) + sizeof(struct elf_phdr) * 2;
    nhdr->p_vaddr = 0;
    nhdr->p_paddr = 0;
    nhdr->p_filesz = 0;
    nhdr->p_memsz = 0;
    nhdr->p_flags = 0;
    nhdr->p_align = 0;

    /* kphdr */
    kphdr = (struct elf_phdr *) wp;
    wp += sizeof(struct elf_phdr);
    kphdr->p_type = PT_LOAD;
    kphdr->p_flags = PF_R;
    kphdr->p_offset = nhdr->p_offset;
    /* kphdr->p_vaddr will be set in handler_pre. */
    kphdr->p_vaddr = 0;
    kphdr->p_paddr = 0;
    kphdr->p_filesz = kphdr->p_memsz = STACKLEN;
    kphdr->p_align = 1;

    /* note0 kpstat */
    if (fill_elf_note (wp, "CORE", NT_PRSTATUS, ((void **)&kpstat),
    sizeof(struct elf_prstatus)) != ELFNOTELEN(sizeof(struct elf_prstatus)))
    return -1;
    wp += ELFNOTELEN(sizeof(struct elf_prstatus));
    nhdr->p_filesz += ELFNOTELEN(sizeof(struct elf_prstatus));
    /* kpstat->pr_reg will be set in handler_pre. */
    memset (kpstat, 0, sizeof(struct elf_prstatus));

    /* note1 pinfo */
    if (fill_elf_note (wp, "CORE", NT_PRPSINFO, ((void **)&pinfo),
    sizeof(struct elf_prpsinfo)) != ELFNOTELEN(sizeof(struct elf_prpsinfo)))
    return -1;
    wp += ELFNOTELEN(sizeof(struct elf_prpsinfo));
    nhdr->p_filesz += ELFNOTELEN(sizeof(struct elf_prpsinfo));
    memset (pinfo, 0, sizeof(struct elf_prpsinfo));
    pinfo->pr_state = 0;
    pinfo->pr_sname = 'R';
    pinfo->pr_zomb = 0;
    snprintf(pinfo->pr_fname, 16, "%s", "vmlinux");
    //snprintf(pinfo->pr_psargs, ELF_PRARGSZ, "%s", saved_command_line);
    snprintf(pinfo->pr_psargs, ELF_PRARGSZ, "%s", "");

    /* kstack */
    kphdr->p_offset += nhdr->p_filesz;
    kstack = wp;
    memset (kstack, 0, STACKLEN);
    wp += STACKLEN;

    /* Check size. */
    if (wp - kbuf != KBUFLEN)
    return -1;

    return 0;
    }

    #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
    static int
    seq_write(struct seq_file *seq, const void *data, size_t len)
    {
    if (seq->count + len < seq->size) {
    memcpy(seq->buf + seq->count, data, len);
    seq->count += len;
    return 0;
    }
    seq->count = seq->size;
    return -1;
    }
    #endif

    static int
    kcoredump_seq_show(struct seq_file *s, void *v)
    {
    return seq_write (s, kbuf, KBUFLEN);
    }

    static int
    kcoredump_proc_open(struct inode *inode, struct file *file)
    {
    return single_open(file, kcoredump_seq_show, NULL);
    }

    static const struct file_operations kcoredump_proc_ops = {
    .owner = THIS_MODULE,
    .open = kcoredump_proc_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release,
    };

    #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
    static inline struct proc_dir_entry *
    proc_create(const char *name, mode_t mode, struct proc_dir_entry *parent,
    const struct file_operations *proc_fops)
    {
    struct proc_dir_entry *ret = create_proc_entry(name, mode, parent);
    if (!ret)
    return NULL;

    ret->proc_fops = proc_fops;

    return ret;
    }
    #endif

    int init_module(void)
    {
    int ret = 0;

    /* Check argument. */
    if (strcmp (kname, KNAME_NOT_SET)) {
    kp.symbol_name = kname;
    kp.offset = koffset;
    }
    else {
    if (kaddr) {
    kp.addr = (kprobe_opcode_t *)kaddr;
    }
    else {
    printk (KERN_ERR "kcoredump: must set the name or addr.\n");
    return -EINVAL;
    }
    }

    if (init_kbuf ()) {
    printk (KERN_ERR "kcoredump: init buf error.\n");
    return -EIO;
    }

    kcoredump_file = proc_create ("kcoredump", 0444, NULL, &kcoredump_proc_ops);
    if (!kcoredump_file) {
    printk (KERN_ERR "kcoredump: create proc file error.\n");
    return -ENOMEM;
    }
    /* kcoredump_file->owner = THIS_MODULE; */
    kcoredump_file->size = KBUFLEN;

    ret = register_kprobe(&kp);
    if (ret < 0) {
    printk (KERN_ERR "kcoredump: register kprobe error.\n");
    remove_proc_entry("kcoredump", NULL);
    return ret;
    }

    printk(KERN_NOTICE "kcoredump: dump in 0x%p.\n", kp.addr);

    return 0;
    }

    void cleanup_module(void)
    {
    unregister_kprobe(&kp);
    remove_proc_entry("kcoredump", NULL);
    }

    module_param_named(name, kname, charp, 0644);
    module_param_named(offset, koffset, uint, 0644);
    module_param_named(addr, kaddr, uint, 0644);

    MODULE_AUTHOR("Hui Zhu");
    MODULE_LICENSE("GPL");
    [unhandled content-type:application/octet-stream]
    \
     
     \ /
      Last update: 2009-09-22 10:51    [W:0.041 / U:34.192 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site