Chinaunix首页 | 论坛 | 博客
  • 博客访问: 170530
  • 博文数量: 33
  • 博客积分: 2143
  • 博客等级: 大尉
  • 技术积分: 807
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-31 10:24
个人简介

Show me the money

文章分类

全部博文(33)

文章存档

2015年(1)

2013年(1)

2011年(12)

2010年(14)

2009年(2)

2008年(2)

2005年(1)

我的朋友

分类: LINUX

2011-03-08 11:42:15

MIPS可以支持3种类型的断点:readwriteexecute。设置watch断点有时候可以调试带来很大的便利。但是,在user mode下,用户程序无法直接操作CP0寄存器,kernel似乎也没有导出相应的接口。在此,作者就抛砖引玉,探讨一种通过proc文件系统向用户程序提供读写watchlo/watchhi寄存器的接口;并通过signal机制将信号传递给用户程序的方案。

 

1. 通过proc文件系统向用户空间提供简单的读写接口。为了使用方便,寄存器的数值以字符串的形式表示。

/*

 * Copyright (C) 2011 Pan Ruochen

 *

 *  This program is free software; you can distribute it and/or modify it

 *  under the terms of the GNU General Public License (Version 2) as

 *  published by the Free Software Foundation.

 *

 *  This program is distributed in the hope 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.

 *

 * Export read/write interfaces to MIPS watchlo/watchhi registers to

 * user space through proc file system.

 *

 */

#include

#include

#include

#include

 

#define __BUILD_READ_WATCH_FUNC(postfix) \

static int proc_read_watch ## postfix(char *buf, char **start, off_t offset,    \

    int size,int *eof, void *data) {                                            \

    if (snprintf(buf, size, "0x%08x\n", (int)read_c0_watch##postfix()) < size)  \

        /* No overflow */                                                       \

        *eof = 1;                                                               \

    return strlen(buf);                                                         \

}

 

__BUILD_READ_WATCH_FUNC(lo0)

__BUILD_READ_WATCH_FUNC(lo1)

__BUILD_READ_WATCH_FUNC(lo2)

__BUILD_READ_WATCH_FUNC(lo3)

__BUILD_READ_WATCH_FUNC(hi0)

__BUILD_READ_WATCH_FUNC(hi1)

__BUILD_READ_WATCH_FUNC(hi2)

__BUILD_READ_WATCH_FUNC(hi3)

 

static int atohex(const char __user *buf, unsigned long count, unsigned long *val)

{

    char tmp[12];

    if( count >= 12 )

        return -ENOMEM;

    if (copy_from_user(tmp, buf, count))

        return -EFAULT;

    sscanf(tmp, "%x", val);

    return 0;

}

 

#define __BUILD_WRITE_WATCH_FUNC(postfix)       \

static int proc_write_watch ## postfix(         \

    struct file *file, const char __user *buf,  \

    unsigned long count,void *data) {           \

    int ret;                                    \

    unsigned long val;                          \

    if( (ret = atohex(buf, count, &val)) < 0 )  \

        return ret;                             \

    write_c0_watch ## postfix (val);            \

    return count;                               \

}

 

__BUILD_WRITE_WATCH_FUNC(lo0)

__BUILD_WRITE_WATCH_FUNC(lo1)

__BUILD_WRITE_WATCH_FUNC(lo2)

__BUILD_WRITE_WATCH_FUNC(lo3)

__BUILD_WRITE_WATCH_FUNC(hi0)

__BUILD_WRITE_WATCH_FUNC(hi1)

__BUILD_WRITE_WATCH_FUNC(hi2)

__BUILD_WRITE_WATCH_FUNC(hi3)

 

int __init mips_watch_proc_init(void)

{

    struct proc_dir_entry *parent, *ent;

    const char *ent_names[] = {

        "watchlo0", "watchhi0",

        "watchlo1", "watchhi1",

        "watchlo2", "watchhi2",

        "watchlo3", "watchhi3",

    };

    const void *ent_read_funcs[] = {

        proc_read_watchlo0, proc_read_watchhi0,

        proc_read_watchlo1, proc_read_watchhi1,

        proc_read_watchlo2, proc_read_watchhi2,

        proc_read_watchlo3, proc_read_watchhi3,

    };

    const void *ent_write_funcs[] = {

        proc_write_watchlo0, proc_write_watchhi0,

        proc_write_watchlo1, proc_write_watchhi1,

        proc_write_watchlo2, proc_write_watchhi2,

        proc_write_watchlo3, proc_write_watchhi3,

    };

    int i;

 

    parent = proc_mkdir("mips_watch", NULL);

    if( parent == NULL ) {

        printk(KERN_WARNING "Failed to register /proc/mips_watch\n");

        return 0;

    }

    for(i=0; i

        ent = create_proc_entry(ent_names[i], 0600, parent);

        if (ent == NULL) {

            printk(KERN_WARNING "Failed to register /proc/mips_watch/%s\n", ent_names[i]);

            return 0;

        }

        ent->read_proc  = ent_read_funcs[i];

        ent->write_proc = ent_write_funcs[i];

    }

    return 0;

}

module_init(mips_watch_proc_init);

 

将这个程序加入kernel,重启系统之后在/proc/mips_watch目录下会出现下列文件

watchlo0     watchhi0    watchlo1    watchhi1

watchlo2     watchlo2    watchlo3    watchhi3

 

通过shell工具就可以访问

/ # cat /proc/mips_watch/watchlo2

0x1e43e740

/ # cat /proc/mips_watch/watchhi2

0x80fa0bf8

/ # echo 0x87654320 > /proc/mips_watch/watchlo2

/ # echo 0xc0000000 > /proc/mips_watch/watchhi2

/ # cat /proc/mips_watch/watchlo2

0x87654320

/ # cat /proc/mips_watch/watchhi2

0xc0000000

 

2. 修改linux kernelwatch异常处理(linux/arch/mips/kernel/traps.c

通过SIGUSR1向用户程序发signal

asmlinkage void do_watch(struct pt_regs *regs)

{

#if 0

    /*

     * We use the watch exception where available to detect stack

     * overflows.

     */

    dump_tlb_all();

    show_regs(regs);

    panic("Caught WATCH exception - probably caused by stack overflow.");

#else /* PRC, Mar 4,2011 */

    unsigned long val;

    die_if_kernel("Caught WATCH exception - probably caused by stack overflow.", regs);

    val = read_c0_watchlo0(); write_c0_watchlo0(val & ~7);

    val = read_c0_watchlo1(); write_c0_watchlo1(val & ~7);

    val = read_c0_watchlo2(); write_c0_watchlo2(val & ~7);

    val = read_c0_watchlo3(); write_c0_watchlo3(val & ~7);

    printk("Force signal %d\n", SIGUSR1);

    force_sig(SIGUSR1, current);

#endif

}

 

3. 用户程序接收signal信号

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define __USE_GNU

#include

 

static void TinyDBG_signal_handler(int signum, siginfo_t* siginfo, ucontext_t *ucontext)

{

    const mcontext_t *const mc = &ucontext->uc_mcontext;

    const unsigned long epc = mc->pc;

 

    fprintf(stderr, "[TINYDBG]: catch exception at 0x%x\n", epc);

    fprintf(stderr, "signum = %d\n", signum);

    exit(1);

}

 

static int register_segv_handler(int sig, void *handler)

{

    int ret = -EINVAL;

    sigset_t signal_set;

    if (sigemptyset(&signal_set) >= 0) {

        struct sigaction sa = {

            .sa_handler  = handler,

            .sa_mask     = signal_set,

            .sa_flags    = SA_SIGINFO,

            .sa_restorer = NULL

        };

        ret = sigaction(sig, &sa, NULL);

        if( ret < 0 )

            fprintf(stderr, "failed to install signal hander.\n");

    }

    return ret;

}

 

void TinyDBG_register_exception_handler(void)

{

    register_segv_handler(SIGUSR1, TinyDBG_signal_handler);

}

 

static int write_to_proc_file(const char *devname, unsigned long val)

{

    int fd, len;

    char buf[12];

 

    fd = open(devname, O_RDWR);

    if(fd == -1) {

        fprintf(stderr, "Cannot open %s\n", devname);

        return -ENOENT;

    }

    sprintf(buf, "0x%x", val);

    len = strlen(buf) + 1;

    fprintf(stderr, "write %s to %s\n", buf, devname);

    write(fd, buf, len);

    close(fd);

    return 0;

}

 

int mips_set_watch(void *addr)

{

    const char *devname1 = "/proc/mips_watch/watchlo2";

    const char *devname2 = "/proc/mips_watch/watchhi2";

    unsigned long val;

    val  = ((int) addr) & ~8;

    val |= 1;

    write_to_proc_file(devname2, 0x40000007);

    write_to_proc_file(devname1, val);

    return 0;

}

 

int main(int argc, char *argv[])

{

    static char buf[24] __attribute__((aligned(8)));

 

    TinyDBG_register_exception_handler();

    memset(buf, 0, 24);

    mips_set_watch(buf);

    buf[0] = 1;

    buf[3] = 2;

    return 0;

}

 

./test_mips_watch

write 0x40000007 to /proc/mips_watch/watchhi2

write 0x410e31 to /proc/mips_watch/watchlo2

[TINYDBG]: catch exception at 0x400af4

signum = 16

 

mips-linux-objdump -d test_mips_watch

00400aac

:

  400aac:   27bdffe0    addiu   sp,sp,-32

  400ab0:   afb10018    sw  s1,24(sp)

  400ab4:   3c110041    lui s1,0x41

  400ab8:   afb00014    sw  s0,20(sp)

  400abc:   afbf001c    sw  ra,28(sp)

  400ac0:   0c1002a7    jal 400a9c

  400ac4:   26300e30    addiu   s0,s1,3632

  400ac8:   02002021    move    a0,s0

  400acc:   ae200e30    sw  zero,3632(s1)

  400ad0:   ae000004    sw  zero,4(s0)

  400ad4:   ae000008    sw  zero,8(s0)

  400ad8:   ae00000c    sw  zero,12(s0)

  400adc:   ae000010    sw  zero,16(s0)

  400ae0:   0c10024d    jal 400934

  400ae4:   ae000014    sw  zero,20(s0)

  400ae8:   8fbf001c    lw  ra,28(sp)

  400aec:   24020002    li  v0,2

  400af0:   24030001    li  v1,1

  400af4:   a2020003    sb  v0,3(s0)

  400af8:   a2230e30    sb  v1,3632(s1)

 

 【原创文章,如需转载请注明原文链接】

 

阅读(2328) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~