实验中用到的源文件
- first_drv.c
在first_drv_init函数中使用如下语句故意产生一个段错误,直接使用物理地址(未使用ioremap进行映射)
gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int first_drv_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置GPF4,5,6为输出 */
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("first_drv_write\n");
copy_from_user(&val, buf, count); // copy_to_user();
if (val == 1)
{
// 点灯
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 灭灯
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
- firstdrvtest.c测试程序
#include
#include
#include
#include
/* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s \n", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
return 0;
}
装载驱动后执行测试程序失败,打印段错误
# ./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3f34000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0 Not tainted (2.6.22.6 #1)
-----------------------------------------------------
PC is at first_drv_open+0x18/0x3c [first_drv]
--------------------------------------------------------
pc就是发生错误的地址,大多时候pc没有那么多详细的信息,只有个地址值,也就是bf000018
--------------------------------------------------------
LR is at chrdev_open+0x14c/0x164
---------------------------------------------
lr是返回地址
------------------------------------------------
pc : [] lr : [] psr: a0000013
sp : c3d45e88 ip : c3d45e98 fp : c3d45e94
r10: 00000000 r9 : c3d44000 r8 : c3dea500
r7 : 00000000 r6 : 00000000 r5 : c3f020c0 r4 : c06f06c0
r3 : bf000000 r2 : 56000050 r1 : bf000964 r0 : 00000000
执行这条导致错误的指令后的各个寄存器的值
----------------------------------------------------------
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 33f34000 DAC: 00000015
Process firstdrvtest (pid: 780, stack limit = 0xc3d44258)
进程ID号
----------------------------------------------------------
Stack: (0xc3d45e88 to 0xc3d46000)
5e80: c3d45ebc c3d45e98 c008d888 bf000010 00000000 c3dea500
5ea0: c3f020c0 c008d73c c0474f20 c3f57dac c3d45ee4 c3d45ec0 c0089e48 c008d74c
5ec0: c3dea500 c3d45f04 00000003 ffffff9c c002c044 c3f2b000 c3d45efc c3d45ee8
5ee0: c0089f64 c0089d58 00000000 00000002 c3d45f68 c3d45f00 c0089fb8 c0089f40
5f00: c3d45f04 c3f57dac c0474f20 00000000 00000000 c3f35000 00000101 00000001
5f20: 00000000 c3d44000 c046d388 c046d380 ffffffe8 c3f2b000 c3d45f68 c3d45f48
5f40: c008a16c c009fc70 00000003 00000000 c3dea500 00000002 be8f2eb0 c3d45f94
5f60: c3d45f6c c008a2f4 c0089f88 00008520 be8f2ea4 0000860c 00008670 00000005
5f80: c002c044 4013365c c3d45fa4 c3d45f98 c008a3a8 c008a2b0 00000000 c3d45fa8
5fa0: c002bea0 c008a394 be8f2ea4 0000860c 00008720 00000002 be8f2eb0 00000001
5fc0: be8f2ea4 0000860c 00008670 00000002 00008520 00000000 4013365c be8f2e78
5fe0: 00000000 be8f2e54 0000266c 400c98e0 60000010 00008720 00000000 00000000
---------------------------
Backtrace:(回溯信息)
内核需要配置此项CONFIG_FRAME_POINTER=y
----------------------------
[] (first_drv_open+0x0/0x3c [first_drv]) from [] (chrdev_open+0x14c/0x164)
[] (chrdev_open+0x0/0x164) from [] (__dentry_open+0x100/0x1e8)
r8:c3f57dac r7:c0474f20 r6:c008d73c r5:c3f020c0 r4:c3dea500
[] (__dentry_open+0x0/0x1e8) from [] (nameidata_to_filp+0x34/0x48)
[] (nameidata_to_filp+0x0/0x48) from [] (do_filp_open+0x40/0x48)
r4:00000002
[] (do_filp_open+0x0/0x48) from [] (do_sys_open+0x54/0xe4)
r5:be8f2eb0 r4:00000002
[] (do_sys_open+0x0/0xe4) from [] (sys_open+0x24/0x28)
[] (sys_open+0x0/0x28) from [] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000)
Segmentation fault
#
- 根据这些信息定位
1.pc属于哪个地址,insmod装载的程序还是内核里的程序
1.1 先看system.map确定是不是内核的函数的地址范围
book@book-desktop:~/system/linux-2.6.22.6$ cat System.map
c0004000 A swapper_pg_dir 起始地址
c03ca954 B _end
book@book-desktop:~/system/linux-2.6.22.6$
- 是哪一个加载的程序
cat /proc/kallsyms从所有函数里面找到一个相近的地址----pc : []
# cat /proc/kallsyms | grep '^b'
bf0000c0 t first_drv_init [first_drv]
bf00016c t first_drv_exit [first_drv]
bf000960 b $d [first_drv]
bf000770 d first_drv_fops [first_drv]
bf000770 d $d [first_drv]
bf00003c t first_drv_write [first_drv]
bf000000 t first_drv_open [first_drv]
bf000000 t $a [first_drv]
bf000038 t $d [first_drv]
bf00003c t $a [first_drv]
bf0000bc t $d [first_drv]
bf0000c0 t $a [first_drv]
bf000140 t $d [first_drv]
bf00096c b firstdrv_class [first_drv]
bf000970 b firstdrv_class_dev [first_drv]
bf00016c t $a [first_drv]
bf0001b0 t $d [first_drv]
bf0008cc d $d [first_drv]
bf000968 b major [first_drv]
bf000964 b gpfcon [first_drv]
bf0007e0 d __this_module [first_drv]
bf0000c0 t init_module [first_drv]
bf00016c t cleanup_module [first_drv]
bf000960 b gpfdat [first_drv]
#
3.bf000000 t first_drv_open [first_drv]
arm-linux-objdump -D first_drv.ko > first_drv.dis
r2 : 56000050
18: e5923000 ldr r3, [r2]这条指令报错
以上是作为模块加载进去的,现在编入内核里面
反汇编内核,直接搜索pc的值,就能找到出错的地址
根据栈信息找出函数的调用关系
1.open函数的栈,sp = c3d45e88 ,一个数值占据4个字节,
调用open的时候,存入4个寄存器
c3d45ebc(fp) c3d45e98(ip) c008d888(lr) bf000010(pc)
根据这个将内核反汇编,查看这个地址的函数是什么c008d888
00000000 :
0: e1a0c00d mov ip, sp
4: e92dd800 stmdb sp!, {fp, ip, lr, pc}
Stack: (0xc3d45e88 to 0xc3d46000)
5e80: c3d45ebc c3d45e98 c008d888 bf000010 00000000 c3dea500
5ea0: c3f020c0 c008d73c c0474f20 c3f57dac c3d45ee4 c3d45ec0 c0089e48 c008d74c
5ec0: c3dea500 c3d45f04 00000003 ffffff9c c002c044 c3f2b000 c3d45efc c3d45ee8
5ee0: c0089f64 c0089d58 00000000 00000002 c3d45f68 c3d45f00 c0089fb8 c0089f40
5f00: c3d45f04 c3f57dac c0474f20 00000000 00000000 c3f35000 00000101 00000001
5f20: 00000000 c3d44000 c046d388 c046d380 ffffffe8 c3f2b000 c3d45f68 c3d45f48
5f40: c008a16c c009fc70 00000003 00000000 c3dea500 00000002 be8f2eb0 c3d45f94
5f60: c3d45f6c c008a2f4 c0089f88 00008520 be8f2ea4 0000860c 00008670 00000005
5f80: c002c044 4013365c c3d45fa4 c3d45f98 c008a3a8 c008a2b0 00000000 c3d45fa8
5fa0: c002bea0 c008a394 be8f2ea4 0000860c 00008720 00000002 be8f2eb0 00000001
5fc0: be8f2ea4 0000860c 00008670 00000002 00008520 00000000 4013365c be8f2e78
5fe0: 00000000 be8f2e54 0000266c 400c98e0 60000010 00008720 00000000 00000000