在一些大型开源软件中,通常会有函数指针的应用,函数指针可以让程序在运行过程中,动态的的去调用对应的回调函数。
linux内核代码中,也有大量的函数指针应用。
例子:
linux中支持大量的文件系统,如ramfs,ext3,ext4,jffs2,procfs,sysfs,nfs,debugfs,等等等,这些文件系统通过VFS虚拟文件系统统一接口,来实现调用具体文件系统的读写接口。这里就会用到函数指针来实现。比如:app在一个目录中创建文件,并向这个文件写入数据。这种情况下,不同的文件系统,会调用不同的read、write接口。因为每种文件系统的设计思想和实现方法都不一样。每种文件系统都有自己的一套读写接口。
问题:当我们debug一个程序时,通过printk等方式定位到程序走进了一个函数指针,而这个函数指针所指向的具体函数可能有好几十个。这种情况下,我们如何知道程序到底跑到哪了呢??
如果采用非常笨的方法,比如,把所有该指针可能指向的函数原型中添加打印,然后执行程序,通过打印信息看到底走了哪一个回调。显然比较慢。
方法:
我们可以通过函数的执行地址+nm工具,来快速定位函数的走向。
(1)在程序调用函数指针前,用printk打印出函数指针的地址。
(2)通过函数符号地址,在elf文件中查找该地址。nm vmlinux | grep 0x81190db0
/tmp # ./renameat2
[ 901.270837] SYSC_renameat2 4500
[ 901.272295] SYSC_renameat2 4507
[ 901.272638] shmem_rename2 3172
[ 901.272941] shmem_rename2 3176
[ 901.274771] vfs_rename 4448 &i-op->rename=0x81190db0
[ 901.276033] vfs_rename 4449 error=0
[ 901.276377] SYSC_renameat2 4601 error=0
/tmp #
-bash-4.1$ nm vmlinux | grep rename | grep 8120c3f0
ffffffff8120c3f0 T simple_rename
-bash-4.1$
ok,已经看到函数调用的具体原型。
这个例子中的的renameat2程序是ltp套件中的测试程序,程序会调用renameat2进入内核,而内核会通过vfs调用到具体文件系统的rename接口。
1、ramfs文件系统的inode操作集
148 static const struct inode_operations ramfs_dir_inode_operations = {
149 .create = ramfs_create,
150 .lookup = simple_lookup,
151 .link = simple_link,
152 .unlink = simple_unlink,
153 .symlink = ramfs_symlink,
154 .mkdir = ramfs_mkdir,
155 .rmdir = simple_rmdir,
156 .mknod = ramfs_mknod,
157 .rename = simple_rename,
158 };
262 static struct file_system_type ramfs_fs_type = {
263 .name = "ramfs",
264 .mount = ramfs_mount,
265 .kill_sb = ramfs_kill_sb,
266 .fs_flags = FS_USERNS_MOUNT,
267 };
2、tmpfs文件系统的inode操作集
3927 static const struct inode_operations shmem_dir_inode_operations = {
3928 #ifdef CONFIG_TMPFS
3929 .create = shmem_create,
3930 .lookup = simple_lookup,
3931 .link = shmem_link,
3932 .unlink = shmem_unlink,
3933 .symlink = shmem_symlink,
3934 .mkdir = shmem_mkdir,
3935 .rmdir = shmem_rmdir,
3936 .mknod = shmem_mknod,
3937 .rename = shmem_rename2,
3938 .tmpfile = shmem_tmpfile,
3939 #endif
3940 #ifdef CONFIG_TMPFS_XATTR
3941 .listxattr = shmem_listxattr,
3942 #endif
3943 #ifdef CONFIG_TMPFS_POSIX_ACL
3944 .setattr = shmem_setattr,
3945 .set_acl = simple_set_acl,
3946 #endif
3947 };
3991 static struct file_system_type shmem_fs_type = {
3992 .owner = THIS_MODULE,
3993 .name = "tmpfs",
3994 .mount = shmem_mount,
3995 .kill_sb = kill_litter_super,
3996 .fs_flags = FS_USERNS_MOUNT,
3997 };
3、ext4fs文件系统的inode操作集
3883 const struct inode_operations ext4_dir_inode_operations = {
3884 .create = ext4_create,
3885 .lookup = ext4_lookup,
3886 .link = ext4_link,
3887 .unlink = ext4_unlink,
3888 .symlink = ext4_symlink,
3889 .mkdir = ext4_mkdir,
3890 .rmdir = ext4_rmdir,
3891 .mknod = ext4_mknod,
3892 .tmpfile = ext4_tmpfile,
3893 .rename = ext4_rename2,
3894 .setattr = ext4_setattr,
3895 .getattr = ext4_getattr,
3896 .listxattr = ext4_listxattr,
3897 .get_acl = ext4_get_acl,
3898 .set_acl = ext4_set_acl,
3899 .fiemap = ext4_fiemap,
3900 };
5790 static struct file_system_type ext4_fs_type = {
5791 .owner = THIS_MODULE,
5792 .name = "ext4",
5793 .mount = ext4_mount,
5794 .kill_sb = kill_block_super,
5795 .fs_flags = FS_REQUIRES_DEV,
5796 };
5797 MODULE_ALIAS_FS("ext4");