将Linux C/C++应用程序从x86平台移植到IBM PowerLinux(2)
- UID
- 852722
|
将Linux C/C++应用程序从x86平台移植到IBM PowerLinux(2)
处理字节顺序
本节将介绍如何识别代码中依赖于字节顺序的区域,并将它们转换为正确的字节顺序格式的方法。
依赖于字节顺序的代码
数据引用上的非一致性是 C 语言的优势,这使得它在编程系统级软件(包括操作系统和设备驱动程序)中变得很流行。此优势包括类型转换、指针操作、union、位字段、结构和灵活的类型检查。但是,同样是这些特性,它们也可能是字节顺序可移植性问题的来源。作为示例,请考虑以下两个代码清单:
清单 1. 使用指针的非一致数据引用
#include <stdio.h>int main(void) { int val; unsigned char *ptr; ptr = (char*) &val; val = 0x89ABCDEF; /* four bytes constant */ printf("%X.%X.%X.%X\n", ptr[0], ptr[1], ptr[2], ptr[3]); exit(0);} 清单 2. 使用 union 的非一致数据引用
#include <stdio.h>union { int val; unsigned char c[sizeof(int)];} u;int main(void) { u.val = 0x89ABCDEF; /* four bytes constant */ printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]); exit(0);} 在 x86 系统上,结果为:
EF.CD.AB.89 在基于 POWER 处理器的系统上,结果为:
89.AB.CD.EF 字节顺序问题表现为 val,它从最重要的字节开始逐个字节地读取数据。
如何确定系统的字节顺序
在 Linux 中,GNU_C 预处理器通常会自动提供一组常用的预定义宏,它们的名称以双下划线开始和结束。一组对确定使用的系统的字节顺序最有用的宏是 __BYTE_ORDER__、__ORDER_LITTLE_ENDIAN__ 和 __ORDER_BIG_ENDIAN__。
/* Test for a big-endian machine */#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__/* Do big endian processing */ 编写字节顺序中立的代码
如果程序模块在跨具有不同字节顺序的平台移植时能够保留其功能,那么它就是字节顺序中立的。换句话说,它的功能与运行它的平台的字节顺序无关。以下是一些编写字节顺序中立的代码的建议:
- 使用宏和指令
要让代码变得可移植,可以使用宏和条件编译指令,如清单 3 和清单 4 所示。 清单 3. 使用指令让字节顺序效果中立化
#include <stdio.h>#define BIG_ENDIAN 1#define LITTLE_ENDIAN 0#define BYTE_ORDER (( htonl(1)==1) ) // returns 1 or 0 depending on platformunion { int val; unsigned char c[sizeof(int)];}u;int main(void) { u.val = 0x89ABCDEF; #if (BYTE_ORDER == BIG_ENDIAN) printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]); #else /*!BYTE_ORDER == BIG_ENDIAN*/ printf("%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]); #endif /*BYTE_ORDER == BIG_ENDIAN*/ exit(0);} 清单 4. 使用宏交换字节(对在运行时确定字节顺序很有用)
// Useful Endian Neutral Macros#include <endian.h> #if __BYTE_ORDER == __BIG_ENDIAN// No translation needed for big endian system#define sw2Bytes(val) val#define sw4Bytes(val) val#define sw8Bytes(val) val#else// Little Endian:Translate// Swap 2 byte, 16 bit values:#define sw2Bytes(val) \ ( (((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00) )// Swap 4 byte, 32 bit values:#define sw4Bytes(val) \ ( (((val) >> 24) & 0x000000FF) | (((val) >> 8) & 0x0000FF00) | \ (((val) << 8) & 0x00FF0000) | (((val) << 24) & 0xFF000000) )// Swap 8 byte, 64 bit values:#define sw8Bytes(val) \ ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \ (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \ (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \ (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) )#endifint main(void) { int a=0x11121314; int b; b = sw4Bytes(a); // b is 0x12 in LE and BE}- 使用编译时选项
实现此目的的另一种方式是在编译器命令行上将 BYTE_ORDER 的值定义为 -DBYTE_ORDER=BIG_ENDIAN。在一个具有不同字节顺序的新平台上编译时,这会消除编辑设备驱动程序或应用程序中的每个文件的需求。只需编译用于构建该驱动程序或应用程序的 makefile。 字节顺序:特定于 Linux
事实证明,Linux 内核提供了一组特定的系统宏,它们可以从低位优先到高位优先和从高位优先到低位优先执行 16、32 和 64 位交换。这些宏对从 Linux x86 移植到 Linux on Power 以及让您大代码对字节顺序中立都很方便,无需担忧会在代码中编写大量 #ifdef __LITTLE_ENDIAN 条件指令。
例如:
cpu_to_le16(u16); // converts CPU endianness 4 bytes to little-endian
le16_to_cpu(u16); // converts little-endian 4 bytes to CPU endianness
这两个宏将一个值从 CPU 使用的字节顺序转换为一种无符号、低位优先、32 位数字,或者执行反向转换。例如,在 Linux on Power 系统上,le16_to_cpu(u16) 将低位优先转换为高位优先,而 cpu_to_le16(u16) 将高位优先转换为低位优先。根据使用的宏和运行的是哪个系统,如果没有工作要做,那么它们将会返回原始值。
这些宏和其他许多宏位于: /usr/include/linux/byteorder/big_endian.h> 和 /usr/include/linux/byteorder/little_endian.h 中。
您可以查看这些头文件来找到您需要的宏。您还可以根据名称中的模式推断出它们的用途。
您可以使用预定义的 GNU_C 预处理器指令,轻松地包含正确的 include 文件,如下所示:
#include <endian.h>#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #include <linux/byteorder/big_endian.h>#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #include <linux/byteorder/little_endian.h>#else // …user defined endian header#endif 其他字节顺序考虑因素
从 x86 移植到 Power 时,不依赖于外部数据文件并且遵守严格的 C ANSI 编程标准的高级应用程序或许能够顺利地移植到 Linux on Power,且不会出现字节顺序问题。但是,字节顺序是一个需要理解的重要概念,而且在代码中识别这些漏洞的能力能够使得向 Linux on Power 的迁移变得很容易。
此外,在下一节中,我们将重点介绍 IBM Linux SDK 中提供的其他代码分析工具,它们可以帮助识别字节顺序问题。 |
|
|
|
|
|