首页 > *nix技术, 跟踪调试 > sparse与__be32

sparse与__be32

2012年7月7日 发表评论 阅读评论 1,003 次浏览

sparse是一个C/C++源文件静态分析工具,官方文档:linux-2.6.38.8/Documentation/sparse.txt
Man手册:http://linux.die.net/man/1/sparse
主页:https://sparse.wiki.kernel.org/index.php/Main_Page

__be32,其定义扩展开为:

#define __bitwise __attribute__((bitwise))
typedef unsigned int __u32;
typedef __u32 __bitwise __be32;

__be32只是一个带有bitwise属性的整型类型,而这个属性对gcc本身没有任何作用,所以如果不利用sparse,__be32和__u32没有任何差别,但是如果利用sparse,它就能提供一种超强制的类型匹配检查。

比如下面这段代码:

int __bitwise i;
int __bitwise j;
…
i = j;

最后一句代码是要被sparse告警的,如下:
CHECK /home/lenky/hello/hello.c
/home/lenky/hello/hello.c:17:3: warning: incorrect type in assignment (different base types)
/home/lenky/hello/hello.c:17:3: expected restricted int i
/home/lenky/hello/hello.c:17:3: got restricted int j

而下面这样的代码就没有问题:

int __bitwise i, j;
…
i = j;

根据上面的这个极端例子可以看到,bitwise属性总是创建一个新的数据类型,所以一般的情况就是把bitwise用在typedef内,如前面看到的__be32类型。由于typedef本身会创建一个新数据类型,所以下面这样的代码是没有问题的:

__be32 i;
__be32 j;
…
i = j;

用在typedef内的bitwise貌似功能一已经发挥不了作用,但是正如其命名所示,它还有功能二,即强制安全位运算。这个怎么理解呢?举例来说,我们知道加法运算不是位运算安全的,因为加法运算会导致位循环移动;但与运算(后面有特例)、比较运算就是位运算安全的:

__be32 i;
__be32 j;
…
i += j;
i &= j;

倒数第二句代码将被sparse如下告警:
/home/lenky/hello/hello.c:17:3: warning: bad assignment (+=) to restricted __be32
另外的就是(特例),如果对一个bitwise的short或char做与运算也是不安全的。因为会导致类型提升,即转为int类型,此时可能会导致符号位改变。简而言之,bitwise的功能二就是保证数据位不丢失或循环移动。

我们的GFP_KERNEL就是一个带有bitwise属性的unsigned类型,所以对于GFP_KERNEL的意外操作都将提示告警:

kmalloc(GFP_KERNEL, size);

像上面这种参数搞反的情况,编译器检查不出来,但是sparse却可以。

除了__be32,还有如下类似:
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;

上面这些宏定义头文件linux/types.h内,对于一个__be32变量i,经过前面的分析可以知道如下几点:
1.不同类型之间的赋值将告警:

__be32 i;
__be32 j;
__le32 k;
int t;
…
i = j;  // ok
i = k;  // warning: incorrect type in assignment (different base types)
i =t;  // warning: incorrect type in assignment (different base types)
t = i;  // warning: incorrect type in assignment (different base types)

2.即使是相同类型,如果操作不是位运算安全的,将告警:

__be32 i;
__be32 j;
…
i = i & j;  // ok
i = i << j;  // warning: incorrect type in assignment (different base types)

3.要对这些类型进行安全运输可以先进行强制转换(另外一个__force属性):

__be32 i;
__be32 j;
__be32 sum;
…
sum = i + j;  // warning: incorrect type in assignment (different base types)
sum = cpu_to_be32(be32_to_cpu(i) + be32_to_cpu(j));  // ok

根据字面意思,__be32等这些类型是用在有字节序(大小端)相关环境的。我们知道网络数据字节序为大端,而我们常用的x86 CPU为小端。假设要打印(printk)一个从网络上接收到数据包的源IP,我们可以这样:

printk(KERN_ALERT "source ip:%d\n", ntohl(iph->saddr));

当然,也可以这样:

printk(KERN_ALERT "source ip:%d\n", __be32_to_cpu(iph->saddr));

而大多数情况一般都是利用ntohl,毕竟这个是公开的接口,而带双下划线的__be32_to_cpu用得就比较少了,事实上ntohl就是__be32_to_cpu的宏定义:

#define ___ntohl(x) __be32_to_cpu(x)
#define ntohl(x) ___ntohl(x)

最后介绍一下Sparse的使用,使用非常简单,编译时输入:

make C=2

即可自动调用Sparse进行分析、检查。如果要单独的使用Sparse(详细选项请看man手册):

sparse hello.c

对于__be32等这些类型,如果没有检查,请注意是否定义了__CHECK_ENDIAN__宏,因为根据头文件linux/types.h内代码,只有当定义了__CHECK_ENDIAN__宏时,__bitwise才有效:

#ifdef __CHECK_ENDIAN__
#define __bitwise __bitwise__
#else
#define __bitwise
#endif

转载请保留地址:http://lenky.info/archives/2012/07/07/1776http://lenky.info/?p=1776


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以或书面等方式告知,本站将及时删除相关内容或链接。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.