sparse静态编译检查工具

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 内核文档

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/80069985