sparse是由Linux之父开发,主要的功能就是静态的检查代码中可能出现的错误,从而减少Linux内核的隐患,这个工具的好处就是能够静态检查代码,尽量在代码编写的最前期发现bug,避免了系统在running状态下才暴露问题,从而提高了debug的效率。
使用方法:
在使用该工具之前,我们需要先安装sparse工具:
sudo apt-get install sparse
当我们在kernel源码环境中执行make help时会发现有如下的提示:
make C=1 [targets] Check all c source with $CHECK (sparse by default)
make C=2 [targets] Force check of all c source with $CHECK
从上面的help提示可知:
当用”make C=1”命令来编译内核,会对所有重新编译的 C 文件使用 sparse 工具进行检查。
当使用”make C=2”命令,无论文件是否被重新编译都会对其使用 sparse 工具进行检查。
(如果你已经编译了内核,用后一种方式可以很快地检查整个源码树)
make C=2 CHECKFLAGS="-D__CHECK_ENDIAN__"
make 的可选变量 CHECKFLAGS 可以用来向 sparse 工具传递参数。编译系统会自动向 sparse 工具传递 -Wbitwise 参数。你可以定义 __CHECK_ENDIAN__ 来进行大小尾检查。这些检查默认都是被关闭的,因为他们通常会产生大量的警告。
运行原理:
想要sparse能够正常功能,必须要在kernel代码中加上对应的处理,也就是gcc attribute属性。
首先看在linux/compiler.h中的定义:
#ifdef __CHECKER__
# define __user __attribute__((noderef, address_space(1)))
# define __kernel __attribute__((address_space(0)))
# define __safe __attribute__((safe))
# define __force __attribute__((force))
# define __nocast __attribute__((nocast))
# define __iomem __attribute__((noderef, address_space(2)))
# define __must_hold(x) __attribute__((context(x,1,1)))
# define __acquires(x) __attribute__((context(x,0,1)))
# define __releases(x) __attribute__((context(x,1,0)))
# define __acquire(x) __context__(x,1)
# define __release(x) __context__(x,-1)
# define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0)
# define __percpu __attribute__((noderef, address_space(3)))
# define __pmem __attribute__((noderef, address_space(5)))
#ifdef CONFIG_SPARSE_RCU_POINTER
# define __rcu __attribute__((noderef, address_space(4)))
#else
# define __rcu
#endif
extern void __chk_user_ptr(const volatile void __user *);
extern void __chk_io_ptr(const volatile void __iomem *);
#else
# define __user
# define __kernel
# define __safe
# define __force
# define __nocast
# define __iomem
# define __chk_user_ptr(x) (void)0
# define __chk_io_ptr(x) (void)0
# define __builtin_warning(x, y...) (1)
# define __must_hold(x)
# define __acquires(x)
# define __releases(x)
# define __acquire(x) (void)0
# define __release(x) (void)0
# define __cond_lock(x,c) (c)
# define __percpu
# define __rcu
# define __pmem
#endif
include/uapi/linux/types.h中也有定义如下:
/*
* Below are truly Linux-specific types that should never collide with
* any application/library that wants linux/types.h.
*/
#ifdef __CHECKER__
#define __bitwise__ __attribute__((bitwise))
#else
#define __bitwise__
#endif
#ifdef __CHECK_ENDIAN__
#define __bitwise __bitwise__
#else
#define __bitwise
#endif
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;
typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;
可以看出,上面的代码,如果有定义CHECKER宏的话(也就是使能了sparse后),那么很多类型都会加上gcc attribut属性。用到的属性如下:
- address_space(num)
- bitwise
- force
- context
我们以bitwise为例做一个介绍,bitwise传给gcc后,在编译过程中会做一个big endian/little endian的类型检查。比如上面定义的:
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
这两条语句代表的是不同的endian,两种类型是不能够混用的,比如把__le32类型的数据强制转换为__be32就会上报WARNING。
另外最后一条,如果在某些场景,我们确实要进行上述类型的转换,为了避免编译时继续上报WARNING,可以使用__force attribute来标识这次转换,由此可以避免编译上报WARNING。
参考:
https://en.wikipedia.org/wiki/Sparse
Documentation/sparse.txt 内核文档