Show me the money
分类: LINUX
2011-03-08 11:42:15
MIPS可以支持3种类型的断点:read,write,execute。设置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 kernel的watch异常处理(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) |
【原创文章,如需转载请注明原文链接】