出现类似问题的原因是所使用的函数在内核中没有被导出符号。在linux-4.0以后的版本中,vfs_read()的符号没有被导出,也就是早期版本中的EXPORT_SYMBOL(vfs_read)这句话没有了。
解决办法:
(1)使用fp->f_op->read()函数,但是使用这个,虽然可以编译通过,但是在加载时,fp->f_op->read的返回值是NULL,原因未找到;
(2)使用修改内核(不建议,会污染内核):在vfs_read()函数后添加EXPORT_SYMBOL(vfs_read);导出符号表
(3)(推荐)使用int kernel_read(struct file *file, loff_t offset, char *addr, unsigned long count)函数:
file:文件; offset:读位址; addr:地址指针; count:读的字节数,返回值是成功返回字节数,失败返回负值;
理由:vfs_read()的定义为ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos),其中,参数__user 表示buf是用户态指针,在内核中是无法使用,因此需要设置使用环境,如下(也就是说,你想直接使用vfs_read这个一句话是不行的,它要用一个代码块才能满足从文件里面读取一些字节的需求,而这个代码块主要是解决__user *buf的使用的问题。),代码块一般如下所示:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(get_ds());
vfs_read(file, (void __user *)addr, count, &pos);
set_fs(old_fs);
相比于vfs_read(),kernel_read()就是通上述操作进行封装(也就是说,kernel_read()把上面代码块组合成了一个新函数,可以给用户直接使用,不需要用户再把上面代码块抄一遍了),因此使用kernel_read更便捷。此外,在linux-4.0以后的版本中取消了对vfs_read()的符号导出,因此无法在编译成模块时使用,而kernel_read()有符号导出,可以在编译成模块时使用。
在linux内核源码linux-4.19.90/fs目录下搜索vfs_read的出处(grep -rn "vfs_read" ./*),得到在
ok@u20:~/data/linux-4.19.90/fs$ grep -rn "vfs_read" ./*
./9p/vfs_addr.c:91: * v9fs_vfs_readpage - read an entire page in from 9P
./9p/vfs_addr.c:98:static int v9fs_vfs_readpage(struct file *filp, struct page *page)
./9p/vfs_addr.c:104: * v9fs_vfs_readpages - read a set of pages from 9P
./9p/vfs_addr.c:113:static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
./9p/vfs_addr.c:341: .readpage = v9fs_vfs_readpage,
./9p/vfs_addr.c:342: .readpages = v9fs_vfs_readpages,
./9p/cache.c:242:static void v9fs_vfs_readpage_complete(struct page *page, void *data,
./9p/cache.c:269: v9fs_vfs_readpage_complete,
./9p/cache.c:307: v9fs_vfs_readpage_complete,
匹配到二进制文件 ./9p/vfs_addr.o
./dax.c:1254: * validated via access_ok() in either vfs_read() or
./exec.c:999: ssize_t res = vfs_read(file, (void __user *)addr, len, &pos);
匹配到二进制文件 ./exec.o
./namei.c:4705: * vfs_readlink - copy symlink body into userspace buffer
./namei.c:4714:int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
./namei.c:4743:EXPORT_SYMBOL(vfs_readlink);
匹配到二进制文件 ./namei.o
./nfsd/nfs4xdr.c:3640: * XXX: By default, vfs_readlink() will truncate symlinks if they
./nfsd/nfs4xdr.c:3642: * easy fix is: if vfs_readlink() precisely fills the buffer, assume
./read_write.c:412:ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
./read_write.c:431: result = vfs_read(file, (void __user *)buf, count, pos);
./read_write.c:437:ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
./read_write.c:452: ret = __vfs_read(file, buf, count, pos);
./read_write.c:579: ret = vfs_read(f.file, buf, count, &pos);
./read_write.c:627: ret = vfs_read(f.file, buf, count, &pos);
./read_write.c:977:ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
./read_write.c:1020: ret = vfs_readv(f.file, vec, vlen, &pos, flags);
./read_write.c:1071: ret = vfs_readv(f.file, vec, vlen, &pos, flags);
匹配到二进制文件 ./read_write.o
./splice.c:362: res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos, 0);
匹配到二进制文件 ./splice.o
./stat.c:406: error = vfs_readlink(path.dentry, buf, bufsiz);
匹配到二进制文件 ./stat.o
./xfs/xfs_ioctl.c:292: error = vfs_readlink(dentry, hreq->ohandle, olen);
得到其定义在read_write.c文件中定义:
可以发现,vfs_read确实没有导出,而kernel_read导出符号了。
原文链接:https://blog.csdn.net/MM_man/article/details/100603683